diff --git a/DEPS b/DEPS
index eac91af77..5382ad0 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'dc87c95382d5beab7fdae2e654d6de568fbe1671',
+  'skia_revision': '135c908812874e1b1ee1b9dcaf7d55544d1f8e2a',
   # 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': '632d0542e57d2553c73892e4f45fd442f155e256',
+  'v8_revision': '10b9a3e5fedf25be365758a2cec66284b3d3874b',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '3b91290ba31e1d5bd0147f44f9cc723a72b6eb9a',
+  'pdfium_revision': 'ce8e51e6c444f6caaa160bf8b1decd5d7ec84e6f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 729b427..9d29982 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -353,6 +353,8 @@
 _ANDROID_SPECIFIC_PYDEPS_FILES = [
     'build/android/test_runner.pydeps',
     'build/android/test_wrapper/logdog_wrapper.pydeps',
+    'build/secondary/third_party/android_platform/'
+        'development/scripts/stack.pydeps',
     'net/tools/testserver/testserver.pydeps',
 ]
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index a5efd6e2..16f5784 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2130,6 +2130,7 @@
     "template_util_unittest.cc",
     "test/histogram_tester_unittest.cc",
     "test/mock_callback_unittest.cc",
+    "test/scoped_feature_list_unittest.cc",
     "test/scoped_mock_time_message_loop_task_runner_unittest.cc",
     "test/scoped_task_scheduler_unittest.cc",
     "test/test_pending_task_unittest.cc",
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index f0f3f4e..22b20b9 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -4,24 +4,79 @@
 
 #include "base/test/scoped_feature_list.h"
 
+#include <algorithm>
 #include <string>
+#include <vector>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 
 namespace base {
 namespace test {
 
 namespace {
 
-static std::string GetFeatureString(
+std::vector<StringPiece> GetFeatureVector(
     const std::initializer_list<base::Feature>& features) {
-  std::string output;
+  std::vector<StringPiece> output;
   for (const base::Feature& feature : features) {
-    if (!output.empty())
-      output += ",";
-    output += feature.name;
+    output.push_back(feature.name);
   }
+
   return output;
 }
 
+// Extracts a feature name from a feature state string. For example, given
+// the input "*MyLovelyFeature<SomeFieldTrial", returns "MyLovelyFeature".
+StringPiece GetFeatureName(StringPiece feature) {
+  StringPiece feature_name = feature;
+
+  // Remove default info.
+  if (feature_name.starts_with("*"))
+    feature_name = feature_name.substr(1);
+
+  // Remove field_trial info.
+  std::size_t index = feature_name.find("<");
+  if (index != std::string::npos)
+    feature_name = feature_name.substr(0, index);
+
+  return feature_name;
+}
+
+struct Features {
+  std::vector<StringPiece> enabled_feature_list;
+  std::vector<StringPiece> disabled_feature_list;
+};
+
+// Merges previously-specified feature overrides with those passed into one of
+// the Init() methods. |features| should be a list of features previously
+// overridden to be in the |override_state|. |merged_features| should contain
+// the enabled and disabled features passed into the Init() method, plus any
+// overrides merged as a result of previous calls to this function.
+void OverrideFeatures(const std::string& features,
+                      base::FeatureList::OverrideState override_state,
+                      Features* merged_features) {
+  std::vector<StringPiece> features_list =
+      SplitStringPiece(features, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+
+  for (StringPiece feature : features_list) {
+    StringPiece feature_name = GetFeatureName(feature);
+
+    if (ContainsValue(merged_features->enabled_feature_list, feature_name) ||
+        ContainsValue(merged_features->disabled_feature_list, feature_name))
+      continue;
+
+    if (override_state == FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE) {
+      merged_features->enabled_feature_list.push_back(feature);
+    } else {
+      DCHECK_EQ(override_state,
+                FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE);
+      merged_features->disabled_feature_list.push_back(feature);
+    }
+  }
+}
+
 }  // namespace
 
 ScopedFeatureList::ScopedFeatureList() {}
@@ -40,13 +95,6 @@
   InitWithFeatureList(std::move(feature_list));
 }
 
-void ScopedFeatureList::InitWithFeatures(
-    const std::initializer_list<base::Feature>& enabled_features,
-    const std::initializer_list<base::Feature>& disabled_features) {
-  InitFromCommandLine(GetFeatureString(enabled_features),
-                      GetFeatureString(disabled_features));
-}
-
 void ScopedFeatureList::InitWithFeatureList(
     std::unique_ptr<FeatureList> feature_list) {
   DCHECK(!original_feature_list_);
@@ -62,12 +110,43 @@
   InitWithFeatureList(std::move(feature_list));
 }
 
+void ScopedFeatureList::InitWithFeatures(
+    const std::initializer_list<base::Feature>& enabled_features,
+    const std::initializer_list<base::Feature>& disabled_features) {
+  Features merged_features;
+  merged_features.enabled_feature_list = GetFeatureVector(enabled_features);
+  merged_features.disabled_feature_list = GetFeatureVector(disabled_features);
+
+  base::FeatureList* feature_list = base::FeatureList::GetInstance();
+
+  // |current_enabled_features| and |current_disabled_features| must declare out
+  // of if scope to avoid them out of scope before JoinString calls because
+  // |merged_features| may contains StringPiece which holding pointer points to
+  // |current_enabled_features| and |current_disabled_features|.
+  std::string current_enabled_features;
+  std::string current_disabled_features;
+  if (feature_list) {
+    base::FeatureList::GetInstance()->GetFeatureOverrides(
+        &current_enabled_features, &current_disabled_features);
+    OverrideFeatures(current_enabled_features,
+                     FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE,
+                     &merged_features);
+    OverrideFeatures(current_disabled_features,
+                     FeatureList::OverrideState::OVERRIDE_DISABLE_FEATURE,
+                     &merged_features);
+  }
+
+  std::string enabled = JoinString(merged_features.enabled_feature_list, ",");
+  std::string disabled = JoinString(merged_features.disabled_feature_list, ",");
+  InitFromCommandLine(enabled, disabled);
+}
+
 void ScopedFeatureList::InitAndEnableFeature(const base::Feature& feature) {
-  InitFromCommandLine(feature.name, std::string());
+  InitWithFeatures({feature}, {});
 }
 
 void ScopedFeatureList::InitAndDisableFeature(const base::Feature& feature) {
-  InitFromCommandLine(std::string(), feature.name);
+  InitWithFeatures({}, {feature});
 }
 
 }  // namespace test
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h
index 99e07f5..e1df6c6a 100644
--- a/base/test/scoped_feature_list.h
+++ b/base/test/scoped_feature_list.h
@@ -22,29 +22,42 @@
   ScopedFeatureList();
   ~ScopedFeatureList();
 
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
   // Initializes and registers a FeatureList instance with no overrides.
   void Init();
 
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
   // Initializes and registers the given FeatureList instance.
   void InitWithFeatureList(std::unique_ptr<FeatureList> feature_list);
 
-  // Initializes and registers a FeatureList instance with the given enabled
-  // and disabled features.
-  void InitWithFeatures(
-      const std::initializer_list<base::Feature>& enabled_features,
-      const std::initializer_list<base::Feature>& disabled_features);
-
-  // Initializes and registers a FeatureList instance with the given
+  // WARNING: This method will reset any globally configured features to their
+  // default values, which can hide feature interaction bugs. Please use
+  // sparingly.  https://crbug.com/713390
+  // Initializes and registers a FeatureList instance with only the given
   // enabled and disabled features (comma-separated names).
   void InitFromCommandLine(const std::string& enable_features,
                            const std::string& disable_features);
 
-  // Initializes and registers a FeatureList instance enabling a single
-  // feature.
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with the given enabled and disabled features.
+  // Any feature overrides already present in the global FeatureList will
+  // continue to apply, unless they conflict with the overrides passed into this
+  // method. This is important for testing potentially unexpected feature
+  // interactions.
+  void InitWithFeatures(
+      const std::initializer_list<base::Feature>& enabled_features,
+      const std::initializer_list<base::Feature>& disabled_features);
+
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with single enabled feature.
   void InitAndEnableFeature(const base::Feature& feature);
 
-  // Initializes and registers a FeatureList instance disabling a single
-  // feature.
+  // Initializes and registers a FeatureList instance based on present
+  // FeatureList and overridden with single disabled feature.
   void InitAndDisableFeature(const base::Feature& feature);
 
  private:
diff --git a/base/test/scoped_feature_list_unittest.cc b/base/test/scoped_feature_list_unittest.cc
new file mode 100644
index 0000000..21d1b4d
--- /dev/null
+++ b/base/test/scoped_feature_list_unittest.cc
@@ -0,0 +1,188 @@
+// 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 "base/test/scoped_feature_list.h"
+
+#include <string>
+#include "base/metrics/field_trial.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace test {
+
+namespace {
+
+const base::Feature kTestFeature1{"TestFeature1",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeature2{"TestFeature2",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
+void ExpectFeatures(const std::string& enabled_features,
+                    const std::string& disabled_features) {
+  base::FeatureList* list = base::FeatureList::GetInstance();
+  std::string actual_enabled_features;
+  std::string actual_disabled_features;
+
+  list->GetFeatureOverrides(&actual_enabled_features,
+                            &actual_disabled_features);
+
+  EXPECT_EQ(enabled_features, actual_enabled_features);
+  EXPECT_EQ(disabled_features, actual_disabled_features);
+}
+
+}  // namespace
+
+class ScopedFeatureListTest : public testing::Test {
+ public:
+  ScopedFeatureListTest() {
+    // Clear default feature list.
+    std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+    feature_list->InitializeFromCommandLine(std::string(), std::string());
+    original_feature_list_ = base::FeatureList::ClearInstanceForTesting();
+    base::FeatureList::SetInstance(std::move(feature_list));
+  }
+
+  ~ScopedFeatureListTest() override {
+    // Restore feature list.
+    if (original_feature_list_) {
+      base::FeatureList::ClearInstanceForTesting();
+      base::FeatureList::RestoreInstanceForTesting(
+          std::move(original_feature_list_));
+    }
+  }
+
+ private:
+  // Save the present FeatureList and restore it after test finish.
+  std::unique_ptr<FeatureList> original_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedFeatureListTest);
+};
+
+TEST_F(ScopedFeatureListTest, BasicScoped) {
+  ExpectFeatures(std::string(), std::string());
+  EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+  {
+    test::ScopedFeatureList feature_list1;
+    feature_list1.InitFromCommandLine("TestFeature1", std::string());
+    ExpectFeatures("TestFeature1", std::string());
+    EXPECT_TRUE(FeatureList::IsEnabled(kTestFeature1));
+  }
+  ExpectFeatures(std::string(), std::string());
+  EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+}
+
+TEST_F(ScopedFeatureListTest, EnableFeatureOverrideDisable) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideNotMakeDuplicate) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature1});
+    ExpectFeatures(std::string(), "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDefault2) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature1});
+    ExpectFeatures(std::string(), "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithEnabledFieldTried) {
+  test::ScopedFeatureList feature_list1;
+
+  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  FieldTrialList field_trial_list(nullptr);
+  FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A");
+  feature_list->RegisterFieldTrialOverride(
+      kTestFeature1.name, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
+  feature_list1.InitWithFeatureList(std::move(feature_list));
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideFeatureWithDisabledFieldTried) {
+  test::ScopedFeatureList feature_list1;
+
+  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  FieldTrialList field_trial_list(nullptr);
+  FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample", "A");
+  feature_list->RegisterFieldTrialOverride(
+      kTestFeature1.name, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
+  feature_list1.InitWithFeatureList(std::move(feature_list));
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature1}, {});
+    ExpectFeatures("TestFeature1", std::string());
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature2});
+    EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature1));
+    EXPECT_FALSE(FeatureList::IsEnabled(kTestFeature2));
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingFeature2) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitWithFeatures({}, {kTestFeature1});
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({kTestFeature2}, {});
+    ExpectFeatures("TestFeature2", "TestFeature1");
+  }
+}
+
+TEST_F(ScopedFeatureListTest, FeatureOverrideKeepsOtherExistingDefaultFeature) {
+  test::ScopedFeatureList feature_list1;
+  feature_list1.InitFromCommandLine("*TestFeature1", std::string());
+
+  {
+    test::ScopedFeatureList feature_list2;
+    feature_list2.InitWithFeatures({}, {kTestFeature2});
+    ExpectFeatures("*TestFeature1", "TestFeature2");
+  }
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/build/android/gyp/create_stack_script.py b/build/android/gyp/create_stack_script.py
new file mode 100755
index 0000000..56556958
--- /dev/null
+++ b/build/android/gyp/create_stack_script.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import argparse
+import os
+import sys
+import textwrap
+
+from util import build_utils
+
+SCRIPT_TEMPLATE = textwrap.dedent(
+    """\
+    #!/usr/bin/env python
+    #
+    # This file was generated by build/android/gyp/create_stack_script.py
+
+    import os
+    import sys
+
+    def main(argv):
+      script_directory = os.path.dirname(__file__)
+      resolve = lambda p: os.path.abspath(os.path.join(script_directory, p))
+      script_path = resolve('{script_path}')
+      script_args = {script_args}
+      script_path_args = {script_path_args}
+      for arg, path in script_path_args:
+        script_args.extend([arg, resolve(path)])
+      script_cmd = [script_path] + script_args + argv
+      print ' '.join(script_cmd)
+      os.execv(script_path, script_cmd)
+
+    if __name__ == '__main__':
+      sys.exit(main(sys.argv[1:]))
+    """)
+
+
+def main(args):
+
+  parser = argparse.ArgumentParser()
+  build_utils.AddDepfileOption(parser)
+  parser.add_argument(
+      '--script-path',
+      help='Path to the wrapped script.')
+  parser.add_argument(
+      '--script-output-path',
+      help='Path to the output script.')
+  group = parser.add_argument_group('Path arguments')
+  group.add_argument('--output-directory')
+  group.add_argument('--packed-libs')
+
+  args, script_args = parser.parse_known_args(build_utils.ExpandFileArgs(args))
+
+  def relativize(p):
+    return os.path.relpath(p, os.path.dirname(args.script_output_path))
+
+  script_path = relativize(args.script_path)
+
+  script_path_args = []
+  if args.output_directory:
+    script_path_args.append(
+        ('--output-directory', relativize(args.output_directory)))
+  if args.packed_libs:
+    for p in build_utils.ParseGnList(args.packed_libs):
+      script_path_args.append(('--packed-lib', relativize(p)))
+
+  with open(args.script_output_path, 'w') as script:
+    script.write(SCRIPT_TEMPLATE.format(
+        script_path=script_path,
+        script_args=script_args,
+        script_path_args=script_path_args))
+
+  os.chmod(args.script_output_path, 0750)
+
+  if args.depfile:
+    build_utils.WriteDepfile(args.depfile, args.script_output_path)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 3f592bd..0ab225b 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -638,6 +638,61 @@
   }
 }
 
+template("stack_script") {
+  forward_variables_from(invoker, [ "testonly" ])
+
+  _stack_target_name = invoker.stack_target_name
+
+  action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data_deps",
+                             "deps",
+                           ])
+    if (!defined(deps)) {
+      deps = []
+    }
+    if (!defined(data_deps)) {
+      data_deps = []
+    }
+
+    data_deps +=
+        [ "//third_party/android_platform/development/scripts:stack_py" ]
+
+    script = "//build/android/gyp/create_stack_script.py"
+    depfile = "$target_gen_dir/$target_name.d"
+
+    _stack_script = "//third_party/android_platform/development/scripts/stack"
+
+    _generated_script = "$root_build_dir/bin/stack_${_stack_target_name}"
+
+    outputs = [
+      _generated_script,
+    ]
+    data = [
+      _generated_script,
+    ]
+
+    args = [
+      "--depfile",
+      rebase_path(depfile, root_build_dir),
+      "--output-directory",
+      rebase_path(root_build_dir, root_build_dir),
+      "--script-path",
+      rebase_path(_stack_script, root_build_dir),
+      "--script-output-path",
+      rebase_path(_generated_script, root_build_dir),
+      "--arch=$target_cpu",
+    ]
+    if (defined(invoker.packed_libraries)) {
+      args += [
+        "--packed-libs",
+        invoker.packed_libraries,
+      ]
+    }
+  }
+}
+
 if (enable_java_templates) {
   import("//build/config/zip.gni")
   import("//third_party/ijar/ijar.gni")
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index eddbbf3..a08a475 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2100,6 +2100,17 @@
         _extra_native_libs_deps +=
             [ "//base/android/linker:chromium_android_linker" ]
       }
+
+      _create_stack_script_rule_name = "${_template_name}__stack_script"
+      _final_deps += [ ":${_create_stack_script_rule_name}" ]
+      stack_script(_create_stack_script_rule_name) {
+        stack_target_name = invoker.target_name
+        deps = _native_libs_deps
+        if (_native_libs_deps != [] && _enable_relocation_packing) {
+          packed_libraries = _native_libs_file_arg
+          deps += [ _native_libs_file_arg_dep ]
+        }
+      }
     }
     if (defined(invoker.loadable_modules) && invoker.loadable_modules != []) {
       _extra_native_libs_even_when_incremental += invoker.loadable_modules
diff --git a/build/print_python_deps.py b/build/print_python_deps.py
index 50a1f08f..92c75a53 100755
--- a/build/print_python_deps.py
+++ b/build/print_python_deps.py
@@ -38,7 +38,8 @@
     if not path.startswith(_SRC_ROOT):
       continue
 
-    if path.endswith('.pyc'):
+    if (path.endswith('.pyc')
+        or (path.endswith('c') and not os.path.splitext(path)[1])):
       path = path[:-1]
     src_paths.add(path)
 
diff --git a/build/secondary/third_party/android_platform/development/scripts/BUILD.gn b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
new file mode 100644
index 0000000..6dabf5ed
--- /dev/null
+++ b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
@@ -0,0 +1,13 @@
+# 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.
+
+group("stack_py") {
+  _py_files = read_file(
+          "//build/secondary/third_party/android_platform/development/scripts/stack.pydeps",
+          "list lines")
+
+  set_sources_assignment_filter([ "#*" ])
+  sources = _py_files
+  data = sources
+}
diff --git a/build/secondary/third_party/android_platform/development/scripts/stack.pydeps b/build/secondary/third_party/android_platform/development/scripts/stack.pydeps
new file mode 100644
index 0000000..2f46729e
--- /dev/null
+++ b/build/secondary/third_party/android_platform/development/scripts/stack.pydeps
@@ -0,0 +1,19 @@
+# Generated by running:
+#   build/print_python_deps.py --root third_party/android_platform/development/scripts --output build/secondary/third_party/android_platform/development/scripts/stack.pydeps third_party/android_platform/development/scripts/stack
+../../../../build/android/pylib/__init__.py
+../../../../build/android/pylib/constants/__init__.py
+../../../../build/android/pylib/symbols/__init__.py
+../../../../build/android/pylib/symbols/elf_symbolizer.py
+../../../catapult/devil/devil/__init__.py
+../../../catapult/devil/devil/android/__init__.py
+../../../catapult/devil/devil/android/constants/__init__.py
+../../../catapult/devil/devil/android/constants/chrome.py
+../../../catapult/devil/devil/android/sdk/__init__.py
+../../../catapult/devil/devil/android/sdk/keyevent.py
+../../../catapult/devil/devil/android/sdk/version_codes.py
+../../../catapult/devil/devil/constants/__init__.py
+../../../catapult/devil/devil/constants/exit_codes.py
+stack
+stack_core.py
+stack_libs.py
+symbol.py
diff --git a/chrome/VERSION b/chrome/VERSION
index 7570bad..39608e4 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=60
 MINOR=0
-BUILD=3084
+BUILD=3085
 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 49f441c..18cb439 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -632,6 +632,14 @@
             android:exported="false">
         </service>
 
+        <!-- Service for decoding images in a sandboxed process. -->
+        <service
+            android:description="@string/decoder_description"
+            android:name="org.chromium.chrome.browser.photo_picker.DecoderService"
+            android:exported="false"
+            android:isolatedProcess="true"
+            android:process=":decoder_service" />
+
         <!-- Providers for chrome data. -->
         <provider android:name="org.chromium.chrome.browser.provider.ChromeBrowserProvider"
             android:authorities="{{ manifest_package }}.ChromeBrowserProvider;{{ manifest_package }}.browser;{{ manifest_package }}"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
index 233abced..cb415ee6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -68,6 +68,8 @@
 
     private static final String DATA_REDUCTION_HAS_EVER_BEEN_ENABLED_PREF =
             "BANDWIDTH_REDUCTION_PROXY_HAS_EVER_BEEN_ENABLED";
+    public static final String DATA_REDUCTION_FIRST_ENABLED_TIME =
+            "BANDWIDTH_REDUCTION_FIRST_ENABLED_TIME";
 
     private static final String PARAM_PERSISTENT_MENU_ITEM_ENABLED = "persistent_menu_item_enabled";
 
@@ -155,6 +157,15 @@
      * data reduction statistics if this is the first time the SPDY proxy has been enabled.
      */
     public void setDataReductionProxyEnabled(Context context, boolean enabled) {
+        if (enabled
+                && ContextUtils.getAppSharedPreferences().getLong(
+                           DATA_REDUCTION_FIRST_ENABLED_TIME, 0)
+                        == 0) {
+            ContextUtils.getAppSharedPreferences()
+                    .edit()
+                    .putLong(DATA_REDUCTION_FIRST_ENABLED_TIME, System.currentTimeMillis())
+                    .apply();
+        }
         ContextUtils.getAppSharedPreferences().edit()
                 .putBoolean(DATA_REDUCTION_ENABLED_PREF, enabled).apply();
         nativeSetDataReductionProxyEnabled(mNativeDataReductionProxySettings, enabled);
@@ -203,12 +214,25 @@
     }
 
     /**
+     * Returns the time that the proxy was first enabled. If data saving statistics are cleared,
+     * this is set to the reset time.
+     * @return The time that the proxy was first enabled in milliseconds since the epoch.
+     */
+    public long getDataReductionProxyFirstEnabledTime() {
+        return ContextUtils.getAppSharedPreferences().getLong(DATA_REDUCTION_FIRST_ENABLED_TIME, 0);
+    }
+
+    /**
      * Clears all data saving statistics.
      */
     public void clearDataSavingStatistics() {
         // When the data saving statistics are cleared, reset the snackbar promo that tells the user
         // how much data they have saved using Data Saver so far.
         DataReductionPromoUtils.saveSnackbarPromoDisplayed(0);
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putLong(DATA_REDUCTION_FIRST_ENABLED_TIME, System.currentTimeMillis())
+                .apply();
         nativeClearDataSavingStatistics(mNativeDataReductionProxySettings);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java
new file mode 100644
index 0000000..ddad8aa
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java
@@ -0,0 +1,110 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.photo_picker;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.FileDescriptor;
+
+/**
+ * A collection of utility functions for dealing with bitmaps.
+ */
+class BitmapUtils {
+    /**
+     * Takes a |bitmap| and returns a square thumbnail of |size|x|size| from the center of the
+     * bitmap specified.
+     * @param bitmap The bitmap to adjust.
+     * @param size The desired size (width and height).
+     * @return The new bitmap thumbnail.
+     */
+    private static Bitmap sizeBitmap(Bitmap bitmap, int size) {
+        // TODO(finnur): Investigate options that require fewer bitmaps to be created.
+        bitmap = ensureMinSize(bitmap, size);
+        bitmap = cropToSquare(bitmap, size);
+        return bitmap;
+    }
+
+    /**
+     * Given a FileDescriptor, decodes the contents and returns a bitmap of
+     * dimensions |size|x|size|.
+     * @param descriptor The FileDescriptor for the file to read.
+     * @param size The width and height of the bitmap to return.
+     * @return The resulting bitmap.
+     */
+    public static Bitmap decodeBitmapFromFileDescriptor(FileDescriptor descriptor, int size) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFileDescriptor(descriptor, null, options);
+        options.inSampleSize = calculateInSampleSize(options.outWidth, options.outHeight, size);
+        options.inJustDecodeBounds = false;
+        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(descriptor, null, options);
+
+        if (bitmap == null) return null;
+
+        return sizeBitmap(bitmap, size);
+    }
+
+    /**
+     * Calculates the sub-sampling factor {@link BitmapFactory#inSampleSize} option for a given
+     * image dimensions, which will be used to create a bitmap of a pre-determined size (as small as
+     * possible without either dimension shrinking below |minSize|.
+     * @param width The calculated width of the image to decode.
+     * @param height The calculated height of the image to decode.
+     * @param minSize The maximum size the image should be (in either dimension).
+     * @return The sub-sampling factor (power of two: 1 = no change, 2 = half-size, etc).
+     */
+    private static int calculateInSampleSize(int width, int height, int minSize) {
+        int inSampleSize = 1;
+        if (width > minSize && height > minSize) {
+            inSampleSize = Math.min(width, height) / minSize;
+        }
+        return inSampleSize;
+    }
+
+    /**
+     * Ensures a |bitmap| is at least |size| in both width and height.
+     * @param bitmap The bitmap to modify.
+     * @param size The minimum size (width and height).
+     * @return The resulting (scaled) bitmap.
+     */
+    private static Bitmap ensureMinSize(Bitmap bitmap, int size) {
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        if (width >= size && height >= size) return bitmap;
+
+        if (width < size) {
+            float scale = (float) size / width;
+            width = size;
+            height *= scale;
+        }
+
+        if (height < size) {
+            float scale = (float) size / height;
+            height = size;
+            width *= scale;
+        }
+
+        return Bitmap.createScaledBitmap(bitmap, width, height, true);
+    }
+
+    /**
+     * Crops a |bitmap| to a certain square |size|
+     * @param bitmap The bitmap to crop.
+     * @param size The size desired (width and height).
+     * @return The resulting (square) bitmap.
+     */
+    private static Bitmap cropToSquare(Bitmap bitmap, int size) {
+        int x = 0;
+        int y = 0;
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+        if (width == size && height == size) return bitmap;
+
+        if (width > size) x = (width - size) / 2;
+        if (height > size) y = (height - size) / 2;
+        return Bitmap.createBitmap(bitmap, x, y, size, size);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
new file mode 100644
index 0000000..0cd7713
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java
@@ -0,0 +1,135 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.photo_picker;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import org.chromium.base.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * A service to accept requests to take image file contents and decode them.
+ */
+public class DecoderService extends Service {
+    // Message ids for communicating with the client.
+
+    // A message sent by the client to decode an image.
+    static final int MSG_DECODE_IMAGE = 1;
+    // A message sent by the server to notify the client of the results of the decoding.
+    static final int MSG_IMAGE_DECODED_REPLY = 2;
+
+    // The keys for the bundle when passing data to and from this service.
+    static final String KEY_FILE_DESCRIPTOR = "file_descriptor";
+    static final String KEY_FILE_PATH = "file_path";
+    static final String KEY_IMAGE_BITMAP = "image_bitmap";
+    static final String KEY_IMAGE_BYTE_COUNT = "image_byte_count";
+    static final String KEY_IMAGE_DESCRIPTOR = "image_descriptor";
+    static final String KEY_SIZE = "size";
+    static final String KEY_SUCCESS = "success";
+
+    // A tag for logging error messages.
+    private static final String TAG = "ImageDecoder";
+
+    /**
+     * Handler for incoming messages from clients.
+     */
+    static class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_DECODE_IMAGE:
+                    Bundle bundle = null;
+                    Messenger client = null;
+                    String filePath = "";
+                    int size = 0;
+                    try {
+                        Bundle payload = msg.getData();
+                        client = msg.replyTo;
+
+                        filePath = payload.getString(KEY_FILE_PATH);
+                        ParcelFileDescriptor pfd = payload.getParcelable(KEY_FILE_DESCRIPTOR);
+                        size = payload.getInt(KEY_SIZE);
+
+                        // Setup a minimum viable response to parent process. Will be fleshed out
+                        // further below.
+                        bundle = new Bundle();
+                        bundle.putString(KEY_FILE_PATH, filePath);
+                        bundle.putBoolean(KEY_SUCCESS, false);
+
+                        FileDescriptor fd = pfd.getFileDescriptor();
+                        Bitmap bitmap = BitmapUtils.decodeBitmapFromFileDescriptor(fd, size);
+                        try {
+                            pfd.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Closing failed " + filePath + " (size: " + size + ") " + e);
+                        }
+
+                        if (bitmap == null) {
+                            Log.e(TAG, "Decode failed " + filePath + " (size: " + size + ")");
+                            sendReply(client, bundle); // Sends SUCCESS == false;
+                            return;
+                        }
+
+                        // The most widely supported, easiest, and reasonably efficient method is to
+                        // decode to an immutable bitmap and just return the bitmap over binder. It
+                        // will internally memcpy itself to ashmem and then just send over the file
+                        // descriptor. In the receiving process it will just leave the bitmap on
+                        // ashmem since it's immutable and carry on.
+                        bundle.putParcelable(KEY_IMAGE_BITMAP, bitmap);
+                        bundle.putBoolean(KEY_SUCCESS, true);
+                        sendReply(client, bundle);
+                        bitmap.recycle();
+                    } catch (Exception e) {
+                        // This service has no UI and maintains no state so if it crashes on
+                        // decoding a photo, it is better UX to eat the exception instead of showing
+                        // a crash dialog and discarding other requests that have already been sent.
+                        Log.e(TAG,
+                                "Unexpected error during decoding " + filePath + " (size: " + size
+                                        + ") " + e);
+
+                        if (bundle != null && client != null) sendReply(client, bundle);
+                    }
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+
+        private void sendReply(Messenger client, Bundle bundle) {
+            Message reply = Message.obtain(null, MSG_IMAGE_DECODED_REPLY);
+            reply.setData(bundle);
+            try {
+                client.send(reply);
+            } catch (RemoteException remoteException) {
+                Log.e(TAG, "Remote error while replying: " + remoteException);
+            }
+        }
+    }
+
+    /**
+     * The target we publish for clients to send messages to IncomingHandler.
+     */
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * When binding to the service, we return an interface to our messenger
+     * for sending messages to the service.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
new file mode 100644
index 0000000..ae83641
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
@@ -0,0 +1,294 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.photo_picker;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.StrictMode;
+
+import org.chromium.base.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.LinkedHashMap;
+
+/**
+ * A class to communicate with the {@link DecoderService}.
+ */
+public class DecoderServiceHost {
+    // A tag for logging error messages.
+    private static final String TAG = "ImageDecoderHost";
+
+    /**
+     * Interface for notifying clients of the service being ready.
+     */
+    public interface ServiceReadyCallback {
+        /**
+         * A function to define to receive a notification once the service is up and running.
+         */
+        void serviceReady();
+    }
+
+    /**
+     * An interface notifying clients when an image has finished decoding.
+     */
+    public interface ImageDecodedCallback {
+        /**
+         * A function to define to receive a notification that an image has been decoded.
+         * @param filePath The file path for the newly decoded image.
+         * @param bitmap The results of the decoding (or placeholder image, if failed).
+         */
+        void imageDecodedCallback(String filePath, Bitmap bitmap);
+    }
+
+    /**
+     * Class for interacting with the main interface of the service.
+     */
+    private class DecoderServiceConnection implements ServiceConnection {
+        // The callback to use to notify the service being ready.
+        private ServiceReadyCallback mCallback;
+
+        public DecoderServiceConnection(ServiceReadyCallback callback) {
+            mCallback = callback;
+        }
+
+        // Called when a connection to the service has been established.
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = new Messenger(service);
+            mBound = true;
+            mCallback.serviceReady();
+        }
+
+        // Called when a connection to the service has been lost.
+        public void onServiceDisconnected(ComponentName name) {
+            mBound = false;
+        }
+    }
+
+    /**
+     * Class for keeping track of the data involved with each request.
+     */
+    private static class DecoderServiceParams {
+        // The path to the file containing the bitmap to decode.
+        public String mFilePath;
+
+        // The requested size (width and height) of the bitmap, once decoded.
+        public int mSize;
+
+        // The callback to use to communicate the results of the decoding.
+        ImageDecodedCallback mCallback;
+
+        public DecoderServiceParams(String filePath, int size, ImageDecodedCallback callback) {
+            mFilePath = filePath;
+            mSize = size;
+            mCallback = callback;
+        }
+    }
+
+    // Map of file paths to decoder parameters in order of request.
+    private LinkedHashMap<String, DecoderServiceParams> mRequests = new LinkedHashMap<>();
+    LinkedHashMap<String, DecoderServiceParams> getRequests() {
+        return mRequests;
+    }
+
+    // The callback used to notify the client when the service is ready.
+    private ServiceReadyCallback mCallback;
+
+    // Messenger for communicating with the remote service.
+    Messenger mService = null;
+
+    // Our service connection to the {@link DecoderService}.
+    private DecoderServiceConnection mConnection;
+
+    // Flag indicating whether we are bound to the service.
+    boolean mBound;
+
+    // The inbound messenger used by the remote service to communicate with us.
+    final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+    /**
+     * The DecoderServiceHost constructor.
+     * @param callback The callback to use when communicating back to the client.
+     */
+    public DecoderServiceHost(ServiceReadyCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Initiate binding with the {@link DecoderService}.
+     * @param context The context to use.
+     */
+    public void bind(Context context) {
+        mConnection = new DecoderServiceConnection(mCallback);
+        Intent intent = new Intent(context, DecoderService.class);
+        context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    /**
+     * Unbind from the {@link DecoderService}.
+     * @param context The context to use.
+     */
+    public void unbind(Context context) {
+        if (mBound) {
+            context.unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    /**
+     * Accepts a request to decode a single image. Queues up the request and reports back
+     * asynchronously on |callback|.
+     * @param filePath The path to the file to decode.
+     * @param size The requested size (width and height) of the resulting bitmap.
+     * @param callback The callback to use to communicate the decoding results.
+     */
+    public void decodeImage(String filePath, int size, ImageDecodedCallback callback) {
+        DecoderServiceParams params = new DecoderServiceParams(filePath, size, callback);
+        mRequests.put(filePath, params);
+        if (mRequests.size() == 1) dispatchNextDecodeImageRequest();
+    }
+
+    /**
+     * Dispatches the next image for decoding (from the queue).
+     */
+    private void dispatchNextDecodeImageRequest() {
+        if (mRequests.entrySet().iterator().hasNext()) {
+            DecoderServiceParams params = mRequests.entrySet().iterator().next().getValue();
+            dispatchDecodeImageRequest(params.mFilePath, params.mSize);
+        }
+    }
+
+    /**
+     * Ties up all the loose ends from the decoding request (communicates the results of the
+     * decoding process back to the client, and takes care of house-keeping chores regarding
+     * the request queue).
+     * @param filePath The path to the image that was just decoded.
+     * @param bitmap The resulting decoded bitmap.
+     */
+    public void closeRequest(String filePath, Bitmap bitmap) {
+        DecoderServiceParams params = getRequests().get(filePath);
+        if (params != null) {
+            params.mCallback.imageDecodedCallback(filePath, bitmap);
+            getRequests().remove(filePath);
+        }
+        dispatchNextDecodeImageRequest();
+    }
+
+    /**
+     * Communicates with the server to decode a single bitmap.
+     * @param filePath The path to the image on disk.
+     * @param size The requested width and height of the resulting bitmap.
+     */
+    private void dispatchDecodeImageRequest(String filePath, int size) {
+        // Obtain a file descriptor to send over to the sandboxed process.
+        File file = new File(filePath);
+        FileInputStream inputFile = null;
+        ParcelFileDescriptor pfd = null;
+        Bundle bundle = new Bundle();
+
+        // The restricted utility process can't open the file to read the
+        // contents, so we need to obtain a file descriptor to pass over.
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            try {
+                inputFile = new FileInputStream(file);
+                FileDescriptor fd = inputFile.getFD();
+                pfd = ParcelFileDescriptor.dup(fd);
+                bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd);
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to obtain FileDescriptor: " + e);
+                closeRequest(filePath, null);
+            }
+        } finally {
+            try {
+                if (inputFile != null) inputFile.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to close inputFile: " + e);
+            }
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
+        if (pfd == null) return;
+
+        // Prepare and send the data over.
+        Message payload = Message.obtain(null, DecoderService.MSG_DECODE_IMAGE);
+        payload.replyTo = mMessenger;
+        bundle.putString(DecoderService.KEY_FILE_PATH, filePath);
+        bundle.putInt(DecoderService.KEY_SIZE, size);
+        payload.setData(bundle);
+        try {
+            mService.send(payload);
+            pfd.close();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Communications failed (Remote): " + e);
+            closeRequest(filePath, null);
+        } catch (IOException e) {
+            Log.e(TAG, "Communications failed (IO): " + e);
+            closeRequest(filePath, null);
+        }
+    }
+
+    /**
+     * Cancels a request to decode an image (if it hasn't already been dispatched).
+     * @param filePath The path to the image to cancel decoding.
+     */
+    public void cancelDecodeImage(String filePath) {
+        mRequests.remove(filePath);
+    }
+
+    /**
+     * A class for handling communications from the service to us.
+     */
+    static class IncomingHandler extends Handler {
+        // The DecoderServiceHost object to communicate with.
+        private final WeakReference<DecoderServiceHost> mHost;
+
+        /**
+         * Constructor for IncomingHandler.
+         * @param host The DecoderServiceHost object to communicate with.
+         */
+        IncomingHandler(DecoderServiceHost host) {
+            mHost = new WeakReference<DecoderServiceHost>(host);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DecoderServiceHost host = mHost.get();
+            if (host == null) {
+                super.handleMessage(msg);
+                return;
+            }
+
+            switch (msg.what) {
+                case DecoderService.MSG_IMAGE_DECODED_REPLY:
+                    Bundle payload = msg.getData();
+
+                    // Read the reply back from the service.
+                    String filePath = payload.getString(DecoderService.KEY_FILE_PATH);
+                    Boolean success = payload.getBoolean(DecoderService.KEY_SUCCESS);
+                    Bitmap bitmap = success
+                            ? (Bitmap) payload.getParcelable(DecoderService.KEY_IMAGE_BITMAP)
+                            : null;
+                    host.closeRequest(filePath, bitmap);
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java
index 2b64157..66cfc45 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java
@@ -10,6 +10,7 @@
 import java.io.File;
 import java.io.FileFilter;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -23,13 +24,12 @@
 
     /**
      * Contructs a MimeTypeFileFilter object.
-     * @param acceptAttr A comma seperated list of MIME types this filter accepts.
-     *                   For example: images/gif, video/*.
+     * @param mimeTypes A list of MIME types this filter accepts.
+     *                  For example: images/gif, video/*.
      */
-    // TODO(finnur): Convert param to List.
-    public MimeTypeFileFilter(@NonNull String acceptAttr) {
-        for (String field : acceptAttr.toLowerCase(Locale.US).split(",")) {
-            field = field.trim();
+    public MimeTypeFileFilter(@NonNull List<String> mimeTypes) {
+        for (String field : mimeTypes) {
+            field = field.trim().toLowerCase(Locale.US);
             if (field.startsWith(".")) {
                 mExtensions.add(field.substring(1));
             } else if (field.endsWith("/*")) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
index 6070779..268ae51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapView.java
@@ -189,8 +189,13 @@
 
         mBitmapDetails = bitmapDetails;
         setItem(bitmapDetails);
-        setThumbnailBitmap(thumbnail);
-        mImageLoaded = !placeholder;
+        if (isCameraTile() || isGalleryTile()) {
+            initializeSpecialTile(mBitmapDetails);
+            mImageLoaded = true;
+        } else {
+            setThumbnailBitmap(thumbnail);
+            mImageLoaded = !placeholder;
+        }
 
         updateSelectionState();
     }
@@ -217,8 +222,6 @@
                 mSpecialTile, null, image, null, null);
         mSpecialTile.setText(labelStringId);
 
-        initialize(bitmapDetails, null, false);
-
         // Reset visibility, since #initialize() sets mSpecialTile visibility to GONE.
         mSpecialTile.setVisibility(View.VISIBLE);
     }
@@ -266,6 +269,7 @@
      * re-used.
      */
     private void resetTile() {
+        mIconView.setImageBitmap(null);
         mUnselectedView.setVisibility(View.GONE);
         mSelectedView.setVisibility(View.GONE);
         mScrim.setVisibility(View.GONE);
@@ -323,15 +327,14 @@
     }
 
     private boolean isGalleryTile() {
-        // TODO(finnur): Remove the null checks here and below.
-        return mBitmapDetails != null && mBitmapDetails.type() == PickerBitmap.GALLERY;
+        return mBitmapDetails.type() == PickerBitmap.GALLERY;
     }
 
     private boolean isCameraTile() {
-        return mBitmapDetails != null && mBitmapDetails.type() == PickerBitmap.CAMERA;
+        return mBitmapDetails.type() == PickerBitmap.CAMERA;
     }
 
     private boolean isPictureTile() {
-        return mBitmapDetails == null || mBitmapDetails.type() == PickerBitmap.PICTURE;
+        return mBitmapDetails.type() == PickerBitmap.PICTURE;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
index 70d18a8..bd8dba0de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java
@@ -5,9 +5,6 @@
 package org.chromium.chrome.browser.photo_picker;
 
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.text.TextUtils;
 
@@ -16,7 +13,8 @@
 /**
  * Holds on to a {@link PickerBitmapView} that displays information about a picker bitmap.
  */
-public class PickerBitmapViewHolder extends ViewHolder {
+public class PickerBitmapViewHolder
+        extends ViewHolder implements DecoderServiceHost.ImageDecodedCallback {
     // Our parent category.
     private PickerCategoryView mCategoryView;
 
@@ -35,11 +33,9 @@
         mItemView = itemView;
     }
 
-    /**
-     * The notification handler for when an image has been decoded.
-     * @param filePath The file path for the newly decoded image.
-     * @param bitmap The results of the decoding (or placeholder image, if failed).
-     */
+    // DecoderServiceHost.ImageDecodedCallback
+
+    @Override
     public void imageDecodedCallback(String filePath, Bitmap bitmap) {
         if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
             return;
@@ -65,31 +61,16 @@
 
         if (mBitmapDetails.type() == PickerBitmap.CAMERA
                 || mBitmapDetails.type() == PickerBitmap.GALLERY) {
-            mItemView.initializeSpecialTile(mBitmapDetails);
+            mItemView.initialize(mBitmapDetails, null, false);
             return;
         }
 
         // TODO(finnur): Use cached image, if available.
 
-        // TODO(finnur): Use decoder instead.
-        int size = mCategoryView.getImageSize();
-        imageDecodedCallback(mBitmapDetails.getFilePath(), createPlaceholderBitmap(size, size));
-    }
+        mItemView.initialize(mBitmapDetails, null, true);
 
-    /**
-     * Creates a placeholder bitmap.
-     * @param width The requested width of the resulting bitmap.
-     * @param height The requested height of the resulting bitmap.
-     * @return Placeholder bitmap.
-     */
-    // TODO(finnur): Remove once the decoder is in place.
-    private Bitmap createPlaceholderBitmap(int width, int height) {
-        Bitmap placeholder = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(placeholder);
-        Paint paint = new Paint();
-        paint.setColor(Color.GRAY);
-        canvas.drawRect(0, 0, (float) width, (float) height, paint);
-        return placeholder;
+        int size = mCategoryView.getImageSize();
+        mCategoryView.getDecoderServiceHost().decodeImage(mBitmapDetails.getFilePath(), size, this);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
index 6c73972..9ed19c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.ui.PhotoPickerListener;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -27,7 +28,8 @@
  * the photo picker, for example the RecyclerView and the bitmap caches.
  */
 public class PickerCategoryView extends RelativeLayout
-        implements FileEnumWorkerTask.FilesEnumeratedCallback, OnMenuItemClickListener {
+        implements FileEnumWorkerTask.FilesEnumeratedCallback, RecyclerView.RecyclerListener,
+                   DecoderServiceHost.ServiceReadyCallback, OnMenuItemClickListener {
     // The dialog that owns us.
     private PhotoPickerDialog mDialog;
 
@@ -46,6 +48,9 @@
     // The callback to notify the listener of decisions reached in the picker.
     private PhotoPickerListener mListener;
 
+    // The host class for the decoding service.
+    private DecoderServiceHost mDecoderServiceHost;
+
     // The RecyclerView showing the images.
     private RecyclerView mRecyclerView;
 
@@ -71,6 +76,9 @@
     // A worker task for asynchronously enumerating files off the main thread.
     private FileEnumWorkerTask mWorkerTask;
 
+    // Whether the connection to the service has been established.
+    private boolean mServiceReady;
+
     public PickerCategoryView(Context context) {
         super(context);
         postConstruction(context);
@@ -84,6 +92,11 @@
     private void postConstruction(Context context) {
         mContext = context;
 
+        mDecoderServiceHost = new DecoderServiceHost(this);
+        mDecoderServiceHost.bind(mContext);
+
+        enumerateBitmaps();
+
         mSelectionDelegate = new SelectionDelegate<PickerBitmap>();
 
         View root = LayoutInflater.from(context).inflate(R.layout.photo_picker_dialog, this);
@@ -107,18 +120,21 @@
         mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(mColumns, mPadding));
 
         // TODO(finnur): Implement caching.
-        // TODO(finnur): Remove this once the decoder service is in place.
-        prepareBitmaps();
     }
 
     /**
-     * Cancels any outstanding requests.
+     * Severs the connection to the decoding utility process and cancels any outstanding requests.
      */
     public void onDialogDismissed() {
         if (mWorkerTask != null) {
             mWorkerTask.cancel(true);
             mWorkerTask = null;
         }
+
+        if (mDecoderServiceHost != null) {
+            mDecoderServiceHost.unbind(mContext);
+            mDecoderServiceHost = null;
+        }
     }
 
     /**
@@ -141,8 +157,25 @@
     @Override
     public void filesEnumeratedCallback(List<PickerBitmap> files) {
         mPickerBitmaps = files;
-        if (files != null && files.size() > 0) {
-            mPickerAdapter.notifyDataSetChanged();
+        processBitmaps();
+    }
+
+    // DecoderServiceHost.ServiceReadyCallback:
+
+    @Override
+    public void serviceReady() {
+        mServiceReady = true;
+        processBitmaps();
+    }
+
+    // RecyclerView.RecyclerListener:
+
+    @Override
+    public void onViewRecycled(RecyclerView.ViewHolder holder) {
+        PickerBitmapViewHolder bitmapHolder = (PickerBitmapViewHolder) holder;
+        String filePath = bitmapHolder.getFilePath();
+        if (filePath != null) {
+            getDecoderServiceHost().cancelDecodeImage(filePath);
         }
     }
 
@@ -162,6 +195,16 @@
         return false;
     }
 
+    /**
+     * Start loading of bitmaps, once files have been enumerated and service is
+     * ready to decode.
+     */
+    private void processBitmaps() {
+        if (mServiceReady && mPickerBitmaps != null) {
+            mPickerAdapter.notifyDataSetChanged();
+        }
+    }
+
     // Simple accessors:
 
     public int getImageSize() {
@@ -176,6 +219,10 @@
         return mPickerBitmaps;
     }
 
+    public DecoderServiceHost getDecoderServiceHost() {
+        return mDecoderServiceHost;
+    }
+
     public boolean isMultiSelectAllowed() {
         return mMultiSelectionAllowed;
     }
@@ -212,14 +259,15 @@
     }
 
     /**
-     * Prepares bitmaps for loading.
+     * Asynchronously enumerates bitmaps on disk.
      */
-    private void prepareBitmaps() {
+    private void enumerateBitmaps() {
         if (mWorkerTask != null) {
             mWorkerTask.cancel(true);
         }
 
-        mWorkerTask = new FileEnumWorkerTask(this, new MimeTypeFileFilter("image/*"));
+        mWorkerTask =
+                new FileEnumWorkerTask(this, new MimeTypeFileFilter(Arrays.asList("image/*")));
         mWorkerTask.execute();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
index f77c23d..20eea20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuFooter.java
@@ -53,12 +53,17 @@
                     DataReductionProxySettings.getInstance()
                             .getContentLengthSavedInHistorySummary());
 
-            long millisSinceEpoch =
+            long chartStartDateInMillisSinceEpoch =
                     DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime()
                     - DateUtils.DAY_IN_MILLIS * ChartDataUsageView.DAYS_IN_CHART;
+            long firstEnabledInMillisSinceEpoch = DataReductionProxySettings.getInstance()
+                                                          .getDataReductionProxyFirstEnabledTime();
+            long mostRecentTime = chartStartDateInMillisSinceEpoch > firstEnabledInMillisSinceEpoch
+                    ? chartStartDateInMillisSinceEpoch
+                    : firstEnabledInMillisSinceEpoch;
+
             final int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR;
-            String date =
-                    DateUtils.formatDateTime(getContext(), millisSinceEpoch, flags).toString();
+            String date = DateUtils.formatDateTime(getContext(), mostRecentTime, flags).toString();
 
             itemText.setText(
                     getContext().getString(R.string.data_reduction_saved_label, dataSaved));
@@ -67,6 +72,12 @@
             int lightActiveColor = ApiCompatibilityUtils.getColor(
                     getContext().getResources(), R.color.light_active_color);
             itemText.setTextColor(lightActiveColor);
+
+            // Reset the icon to blue.
+            ImageView icon = (ImageView) findViewById(R.id.chart_icon);
+            LayerDrawable layers = (LayerDrawable) icon.getDrawable();
+            Drawable chart = layers.findDrawableByLayerId(R.id.main_menu_chart);
+            chart.setColorFilter(null);
         } else {
             DataReductionProxyUma.dataReductionProxyUIAction(
                     DataReductionProxyUma.ACTION_MAIN_MENU_DISPLAYED_OFF);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index ade32fc..4caca00c 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2816,6 +2816,11 @@
         <ph name="BEGIN_LINK">&lt;link&gt;</ph>Get help<ph name="END_LINK">&lt;/link&gt;</ph>
       </message>
 
+      <!-- Photo Picker strings -->
+      <message name="IDS_DECODER_DESCRIPTION" desc="The title for the image decoder utility service.">
+        Image decoder
+      </message>
+
       <!-- Migration strings -->
       <message name="IDS_TAB_SWITCHER_CALLOUT_BODY" desc="Indicates that clicking the tab switcher button gives you quick access to your tabs.">
         Tap this button for quick access to your tabs.
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index d1d08c6..7764a071 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -784,6 +784,9 @@
   "java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java",
   "java/src/org/chromium/chrome/browser/permissions/PermissionDialogDelegate.java",
   "java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java",
+  "java/src/org/chromium/chrome/browser/photo_picker/BitmapUtils.java",
+  "java/src/org/chromium/chrome/browser/photo_picker/DecoderService.java",
+  "java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java",
   "java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java",
   "java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java",
   "java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java",
@@ -1628,6 +1631,7 @@
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java",
   "javatests/src/org/chromium/chrome/browser/widget/findinpage/FindTest.java",
   "javatests/src/org/chromium/chrome/test/ParametersOnMultiTest.java",
+  "javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java",
   "javatests/src/org/chromium/chrome/test/util/ChromeSigninUtilsTest.java",
   "javatests/src/org/chromium/chrome/test/util/parameters/SigninParametersTest.java",
 ]
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
index c4cf630..b819925 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
@@ -125,6 +125,7 @@
      */
     @LargeTest
     @Feature({"CopylessPaste"})
+    @DisabledTest(message = "crbug.com/713895")
     public void testNoMeta() throws InterruptedException, TimeoutException {
         loadUrl(mTestServer.getURL(NODATA_PAGE));
         mCallbackHelper.waitForCallback(0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java b/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java
new file mode 100644
index 0000000..3988cb0
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.crash;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+
+/** Tests that intentionally crash in different ways.
+ *
+ *  These are all purposefully disabled and should only be run manually.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
+public class IntentionalCrashTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule(ChromeActivity.class);
+
+    @DisabledTest
+    @Test
+    public void testRendererCrash() {
+        try {
+            mActivityTestRule.startMainActivityWithURL("chrome://crash");
+        } catch (InterruptedException e) {
+            Assert.fail(e.toString());
+        }
+    }
+
+    @DisabledTest
+    @Test
+    public void testBrowserCrash() {
+        try {
+            mActivityTestRule.startMainActivityWithURL("chrome://inducebrowsercrashforrealz");
+        } catch (InterruptedException e) {
+            Assert.fail(e.toString());
+        }
+    }
+
+    @DisabledTest
+    @Test
+    public void testJavaCrash() {
+        try {
+            mActivityTestRule.startMainActivityWithURL("chrome://java-crash");
+        } catch (InterruptedException e) {
+            Assert.fail(e.toString());
+        }
+    }
+
+    @DisabledTest
+    @Test
+    public void testGpuCrash() {
+        try {
+            mActivityTestRule.startMainActivityWithURL("chrome://gpucrash");
+        } catch (InterruptedException e) {
+            Assert.fail(e.toString());
+        }
+    }
+}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 14c1ced..41c56ba 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -561,20 +561,6 @@
 };
 #endif  // OS_CHROMEOS
 
-#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \
-    defined(OS_WIN)
-const FeatureEntry::Choice kAppMenuIconChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-    {flag_descriptions::kAppMenuIconOldBehavior, switches::kAppMenuIcon,
-     switches::kAppMenuIconOldBehavior},
-    {flag_descriptions::kAppMenuIconPersistentOpenedState,
-     switches::kAppMenuIcon, switches::kAppMenuIconPersistentOpenedState},
-    {flag_descriptions::kAppMenuIconPersistentClosedState,
-     switches::kAppMenuIcon, switches::kAppMenuIconPersistentClosedState},
-};
-#endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
-        // defined(OS_WIN)
-
 #if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam
     kContentSuggestionsCategoryOrderFeatureVariationGeneral[] = {
@@ -2638,9 +2624,9 @@
      flag_descriptions::kOmniboxEntitySuggestionsName,
      flag_descriptions::kOmniboxEntitySuggestionsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxEntitySuggestions)},
-    {"app-menu-icon", flag_descriptions::kAppMenuIconName,
-     flag_descriptions::kAppMenuIconDescription, kOsDesktop,
-     MULTI_VALUE_TYPE(kAppMenuIconChoices)},
+    {"enable-new-app-menu-icon", flag_descriptions::kEnableNewAppMenuIconName,
+     flag_descriptions::kEnableNewAppMenuIconDescription, kOsDesktop,
+     SINGLE_VALUE_TYPE(switches::kEnableNewAppMenuIcon)},
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
         // defined(OS_WIN)
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e3e4ea7..458b7189 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2783,11 +2783,9 @@
 const char kPauseBackgroundTabsDescription[] =
     "Pause timers in background tabs after 5 minutes on desktop.";
 
-const char kAppMenuIconName[] = "App Menu Icon";
-const char kAppMenuIconDescription[] = "Changes the icon in the app menu.";
-const char kAppMenuIconOldBehavior[] = "Old Behavior";
-const char kAppMenuIconPersistentOpenedState[] = "Persistent Opened State";
-const char kAppMenuIconPersistentClosedState[] = "Persistent Closed State";
+const char kEnableNewAppMenuIconName[] = "Enable the New App Menu Icon";
+const char kEnableNewAppMenuIconDescription[] =
+    "Use the new app menu icon with update notification animations.";
 
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
         // defined(OS_WIN)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 0e71765..8cc1b352 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3016,20 +3016,11 @@
 extern const char kPauseBackgroundTabsName[];
 extern const char kPauseBackgroundTabsDescription[];
 
-// Name of the flag to change the app menu icon.
-extern const char kAppMenuIconName[];
+// Name of the flag to enable the new app menu icon.
+extern const char kEnableNewAppMenuIconName[];
 
-// Description of the flag to change the app menu icon.
-extern const char kAppMenuIconDescription[];
-
-// Description of the app menu icon's old appearance.
-extern const char kAppMenuIconOldBehavior[];
-
-// Description of the app menu animated icon's persistent opened state.
-extern const char kAppMenuIconPersistentOpenedState[];
-
-// Description of the app menu animated icon's persistent closed state.
-extern const char kAppMenuIconPersistentClosedState[];
+// Description of the flag to enable the new app menu icon.
+extern const char kEnableNewAppMenuIconDescription[];
 
 #endif  // defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) ||
         // defined(OS_WIN)
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 6c19a60..82954ac 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -326,15 +326,20 @@
     /** @override */
     resetCategoryPermissionForOrigin: function(
         primaryPattern, secondaryPattern, contentType, incognito) {
-      chrome.send('resetCategoryPermissionForOrigin',
+      chrome.send(
+          'resetCategoryPermissionForOrigin',
           [primaryPattern, secondaryPattern, contentType, incognito]);
     },
 
     /** @override */
     setCategoryPermissionForOrigin: function(
         primaryPattern, secondaryPattern, contentType, value, incognito) {
-      chrome.send('setCategoryPermissionForOrigin',
-          [primaryPattern, secondaryPattern, contentType, value, incognito]);
+      // TODO(dschuyler): It may be incorrect for JS to send the embeddingOrigin
+      // pattern. Look into removing this parameter from site_settings_handler.
+      // Ignoring the |secondaryPattern| and using '' instead is a quick-fix.
+      chrome.send(
+          'setCategoryPermissionForOrigin',
+          [primaryPattern, '', contentType, value, incognito]);
     },
 
     /** @override */
diff --git a/chrome/browser/ui/views/toolbar/app_menu_animation.cc b/chrome/browser/ui/views/toolbar/app_menu_animation.cc
index 8d643ca..01d0b5a 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_animation.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu_animation.cc
@@ -60,7 +60,7 @@
 
 void AppMenuAnimation::AppMenuDot::Paint(const gfx::PointF& center_point,
                                          SkColor start_color,
-                                         SkColor severity_color,
+                                         SkColor target_color,
                                          gfx::Canvas* canvas,
                                          const gfx::Rect& bounds,
                                          const gfx::SlideAnimation* animation) {
@@ -110,8 +110,8 @@
   point.Offset(-dot_width / 2, -dot_height / 2);
 
   SkColor color = is_opening ? gfx::Tween::ColorValueBetween(
-                                   color_progress, start_color, severity_color)
-                             : severity_color;
+                                   color_progress, start_color, target_color)
+                             : target_color;
 
   cc::PaintFlags flags;
   flags.setColor(color);
@@ -124,10 +124,8 @@
   canvas->DrawRoundRect(gfx::RectF(point, dot_size), 2.0, flags);
 }
 
-AppMenuAnimation::AppMenuAnimation(AppMenuButton* owner,
-                                   bool should_animate_closed)
+AppMenuAnimation::AppMenuAnimation(AppMenuButton* owner, SkColor initial_color)
     : owner_(owner),
-      should_animate_closed_(should_animate_closed),
       animation_(base::MakeUnique<gfx::SlideAnimation>(this)),
       bottom_dot_(base::TimeDelta(),
                   kBottomWidthOpenInterval,
@@ -138,8 +136,8 @@
       top_dot_(base::TimeDelta::FromMilliseconds(kDotDelayMs * 2),
                kTopWidthOpenInterval,
                kTopStrokeOpenInterval),
-      start_color_(gfx::kPlaceholderColor),
-      severity_color_(gfx::kPlaceholderColor) {
+      start_color_(initial_color),
+      target_color_(initial_color) {
   animation_->SetSlideDuration(kOpenDurationMs);
   animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
 }
@@ -156,20 +154,14 @@
   gfx::PointF bottom_point = middle_point;
   bottom_point.Offset(0, y_offset);
 
-  middle_dot_.Paint(middle_point, start_color_, severity_color_, canvas, bounds,
+  middle_dot_.Paint(middle_point, start_color_, target_color_, canvas, bounds,
                     animation_.get());
-  top_dot_.Paint(top_point, start_color_, severity_color_, canvas, bounds,
+  top_dot_.Paint(top_point, start_color_, target_color_, canvas, bounds,
                  animation_.get());
-  bottom_dot_.Paint(bottom_point, start_color_, severity_color_, canvas, bounds,
+  bottom_dot_.Paint(bottom_point, start_color_, target_color_, canvas, bounds,
                     animation_.get());
 }
 
-void AppMenuAnimation::SetIconColors(SkColor start_color,
-                                     SkColor severity_color) {
-  start_color_ = start_color;
-  severity_color_ = severity_color;
-}
-
 void AppMenuAnimation::StartAnimation() {
   if (!animation_->is_animating()) {
     animation_->SetSlideDuration(kOpenDurationMs);
@@ -179,15 +171,13 @@
 }
 
 void AppMenuAnimation::AnimationEnded(const gfx::Animation* animation) {
-  if (animation_->IsShowing() && should_animate_closed_) {
+  if (animation_->IsShowing()) {
     animation_->SetSlideDuration(kCloseDurationMs);
     animation_->Hide();
-    return;
+  } else {
+    start_color_ = target_color_;
   }
 
-  if (!animation_->IsShowing())
-    start_color_ = severity_color_;
-
   owner_->AppMenuAnimationEnded();
 }
 
diff --git a/chrome/browser/ui/views/toolbar/app_menu_animation.h b/chrome/browser/ui/views/toolbar/app_menu_animation.h
index 1ff69ee..122b1c97 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_animation.h
+++ b/chrome/browser/ui/views/toolbar/app_menu_animation.h
@@ -20,15 +20,14 @@
 // This class is used for animating and drawing the app menu icon.
 class AppMenuAnimation : public gfx::AnimationDelegate {
  public:
-  AppMenuAnimation(AppMenuButton* owner, bool should_animate_closed);
+  AppMenuAnimation(AppMenuButton* owner, SkColor initial_color);
 
   ~AppMenuAnimation() override;
 
   // Paints the app menu icon.
   void PaintAppMenu(gfx::Canvas* canvas, const gfx::Rect& bounds);
 
-  // Updates the icon colors.
-  void SetIconColors(SkColor start_color, SkColor severity_color);
+  void set_target_color(SkColor target_color) { target_color_ = target_color; }
 
   // Starts the animation if it's not already running.
   void StartAnimation();
@@ -70,9 +69,6 @@
 
   AppMenuButton* const owner_;
 
-  // True if the animation should close after it finishes opening.
-  const bool should_animate_closed_;
-
   std::unique_ptr<gfx::SlideAnimation> animation_;
 
   AppMenuDot bottom_dot_;
@@ -80,12 +76,12 @@
   AppMenuDot top_dot_;
 
   // The starting color of the dots. The animation is expected to transition
-  // from this color to |severity_color_|.
+  // from this color to |target_color_|.
   SkColor start_color_;
 
   // The severity color of the dots. This is final color at the end of the
   // animation.
-  SkColor severity_color_;
+  SkColor target_color_;
 
   DISALLOW_COPY_AND_ASSIGN(AppMenuAnimation);
 };
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc
index 6b3b5bd..272cc0d 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc
@@ -47,22 +47,16 @@
       severity_(AppMenuIconController::Severity::NONE),
       type_(AppMenuIconController::IconType::NONE),
       toolbar_view_(toolbar_view),
+      should_use_new_icon_(false),
       margin_trailing_(0),
       weak_factory_(this) {
   SetInkDropMode(InkDropMode::ON);
   SetFocusPainter(nullptr);
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kAppMenuIcon)) {
-    std::string flag =
-        command_line->GetSwitchValueASCII(switches::kAppMenuIcon);
-    if (flag == switches::kAppMenuIconPersistentClosedState) {
-      Browser* browser = toolbar_view_->browser();
-      browser->tab_strip_model()->AddObserver(this);
-      animation_ = base::MakeUnique<AppMenuAnimation>(this, true);
-    } else if (flag == switches::kAppMenuIconPersistentOpenedState) {
-      animation_ = base::MakeUnique<AppMenuAnimation>(this, false);
-    }
+  if (command_line->HasSwitch(switches::kEnableNewAppMenuIcon)) {
+    toolbar_view_->browser()->tab_strip_model()->AddObserver(this);
+    should_use_new_icon_ = true;
   }
 }
 
@@ -109,8 +103,7 @@
                         base::TimeTicks::Now() - menu_open_time);
   }
 
-  if (animation_ && severity_ != AppMenuIconController::Severity::NONE)
-    animation_->StartAnimation();
+  AnimateIconIfPossible();
 }
 
 void AppMenuButton::CloseMenu() {
@@ -162,8 +155,7 @@
                                   content::WebContents* contents,
                                   int index,
                                   bool foreground) {
-  if (severity_ != AppMenuIconController::Severity::NONE)
-    animation_->StartAnimation();
+  AnimateIconIfPossible();
 }
 
 void AppMenuButton::UpdateIcon(bool should_animate) {
@@ -189,10 +181,14 @@
       break;
   }
 
-  if (animation_) {
-    animation_->SetIconColors(toolbar_icon_color, severity_color);
+  if (should_use_new_icon_) {
+    if (!animation_)
+      animation_ = base::MakeUnique<AppMenuAnimation>(this, toolbar_icon_color);
+
+    animation_->set_target_color(severity_color);
     if (should_animate)
-      animation_->StartAnimation();
+      AnimateIconIfPossible();
+
     return;
   }
 
@@ -221,6 +217,15 @@
   InvalidateLayout();
 }
 
+void AppMenuButton::AnimateIconIfPossible() {
+  if (!animation_ || !should_use_new_icon_ ||
+      severity_ == AppMenuIconController::Severity::NONE) {
+    return;
+  }
+
+  animation_->StartAnimation();
+}
+
 void AppMenuButton::AppMenuAnimationStarted() {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.h b/chrome/browser/ui/views/toolbar/app_menu_button.h
index 06167a7..96290b4 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/app_menu_button.h
@@ -72,6 +72,10 @@
   // to make the focus rectangle centered.
   void SetTrailingMargin(int margin);
 
+  // Animates the icon if possible. The icon will not animate if the severity
+  // level is none, |animation_| is nullptr or |should_use_new_icon_| is false.
+  void AnimateIconIfPossible();
+
   // Methods called by AppMenuAnimation when the animation has started/ended.
   // The layer is managed inside these methods.
   void AppMenuAnimationStarted();
@@ -115,6 +119,9 @@
   // Used for animating and drawing the app menu icon.
   std::unique_ptr<AppMenuAnimation> animation_;
 
+  // True if the app menu should use the new animated icon.
+  bool should_use_new_icon_;
+
   // Any trailing margin to be applied. Used when the browser is in
   // a maximized state to extend to the full window width.
   int margin_trailing_;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 53bcd053..c8a32d02 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -1139,12 +1139,7 @@
 
 #if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \
     defined(OS_WIN)
-extern const char kAppMenuIcon[] = "app-menu-icon";
-extern const char kAppMenuIconOldBehavior[] = "old-behavior";
-extern const char kAppMenuIconPersistentOpenedState[] =
-    "persistent-opened-state";
-extern const char kAppMenuIconPersistentClosedState[] =
-    "persistent-closed-state";
+extern const char kEnableNewAppMenuIcon[] = "enable-new-app-menu-icon";
 #endif
 
 bool ExtensionsDisabled(const base::CommandLine& command_line) {
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 66adeb74..a8b2b351 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -355,10 +355,7 @@
 
 #if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \
     defined(OS_WIN)
-extern const char kAppMenuIcon[];
-extern const char kAppMenuIconOldBehavior[];
-extern const char kAppMenuIconPersistentOpenedState[];
-extern const char kAppMenuIconPersistentClosedState[];
+extern const char kEnableNewAppMenuIcon[];
 #endif
 
 bool ExtensionsDisabled(const base::CommandLine& command_line);
diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc
index 0d50484f..8d8faeb 100644
--- a/components/nacl/renderer/ppb_nacl_private_impl.cc
+++ b/components/nacl/renderer/ppb_nacl_private_impl.cc
@@ -461,8 +461,7 @@
   if (nexe_file_info->handle != PP_kInvalidFileHandle)
     nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
 #elif defined(OS_WIN)
-  nexe_for_transit = IPC::PlatformFileForTransit(nexe_file_info->handle,
-                                                 base::GetCurrentProcId());
+  nexe_for_transit = IPC::PlatformFileForTransit(nexe_file_info->handle);
 #else
 # error Unsupported target platform.
 #endif
diff --git a/components/variations/variations_seed_simulator.cc b/components/variations/variations_seed_simulator.cc
index ea56739..59b56643 100644
--- a/components/variations/variations_seed_simulator.cc
+++ b/components/variations/variations_seed_simulator.cc
@@ -43,7 +43,7 @@
   scoped_refptr<base::FieldTrial> trial(
       base::FieldTrial::CreateSimulatedFieldTrial(
           study.name(), processed_study.total_probability(),
-          study.default_experiment_name(), entropy_value));
+          processed_study.GetDefaultExperimentName(), entropy_value));
 
   for (int i = 0; i < study.experiment_size(); ++i) {
     const Study_Experiment& experiment = study.experiment(i);
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index 5108aa8..49def8d1 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -109,6 +109,11 @@
   return;
 #endif
 
+#if defined(OS_ANDROID)
+  // See https://crbug.com/653864.
+  return;
+#endif
+
   content::RenderThreadImpl* const render_thread_impl =
       content::RenderThreadImpl::current();
   if (!render_thread_impl) {
@@ -127,17 +132,12 @@
       gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles();
   for (const auto& supported_profile : vea_supported_profiles) {
     for (auto& codec_id_and_profile : kPreferredCodecIdAndVEAProfiles) {
-      const media::VideoCodecProfile codec = supported_profile.profile;
-#if defined(OS_ANDROID)
-      // TODO(mcasas): enable other codecs, https://crbug.com/653864.
-      if (codec < media::VP8PROFILE_MIN || codec > media::VP8PROFILE_MAX)
-        continue;
-#endif
-      if (codec >= codec_id_and_profile.min_profile &&
-          codec <= codec_id_and_profile.max_profile) {
-        DVLOG(2) << "Accelerated codec found: " << media::GetProfileName(codec);
-        codec_id_to_profile_.insert(
-            std::make_pair(codec_id_and_profile.codec_id, codec));
+      if (supported_profile.profile >= codec_id_and_profile.min_profile &&
+          supported_profile.profile <= codec_id_and_profile.max_profile) {
+        DVLOG(2) << "Accelerated codec found: "
+                 << media::GetProfileName(supported_profile.profile);
+        codec_id_to_profile_.insert(std::make_pair(
+            codec_id_and_profile.codec_id, supported_profile.profile));
       }
     }
   }
diff --git a/content/renderer/media_recorder/video_track_recorder.h b/content/renderer/media_recorder/video_track_recorder.h
index e53cef4..53a871f 100644
--- a/content/renderer/media_recorder/video_track_recorder.h
+++ b/content/renderer/media_recorder/video_track_recorder.h
@@ -32,13 +32,8 @@
 }  // namespace media
 
 namespace video_track_recorder {
-#if defined(OS_ANDROID)
-const int kVEAEncoderMinResolutionWidth = 176;
-const int kVEAEncoderMinResolutionHeight = 144;
-#else
 const int kVEAEncoderMinResolutionWidth = 640;
 const int kVEAEncoderMinResolutionHeight = 480;
-#endif
 }  // namespace video_track_recorder
 
 namespace content {
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 1bc789e..4ec332b 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -256,8 +256,6 @@
         ['mac'], bug=1832) # khronos WebGL issue
 
     # Mac Retina NVIDIA
-    self.Fail('conformance/textures/misc/cube-map-uploads-out-of-order.html',
-        ['mac', ('nvidia', 0xfe9)], bug=473739)
     self.Fail('deqp/functional/gles3/fbomultisample*',
         ['mac', ('nvidia', 0xfe9)], bug=641209)
     self.Fail('deqp/functional/gles3/framebufferblit/' +
diff --git a/device/usb/usb_descriptors.cc b/device/usb/usb_descriptors.cc
index 645ee56..ae25c1b7 100644
--- a/device/usb/usb_descriptors.cc
+++ b/device/usb/usb_descriptors.cc
@@ -41,7 +41,7 @@
 const uint8_t kEndpointDescriptorLength = 7;
 const uint8_t kInterfaceAssociationDescriptorLength = 8;
 
-const int kControlTransferTimeout = 60000;  // 1 minute
+const int kControlTransferTimeoutMs = 2000;  // 2 seconds
 
 struct UsbInterfaceAssociationDescriptor {
   UsbInterfaceAssociationDescriptor(uint8_t first_interface,
@@ -123,7 +123,7 @@
         UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
         UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
         kConfigurationDescriptorType << 8 | index, 0, buffer, total_length,
-        kControlTransferTimeout,
+        kControlTransferTimeoutMs,
         base::Bind(&OnReadConfigDescriptor, desc, closure));
   } else {
     LOG(ERROR) << "Failed to read length for configuration "
@@ -169,7 +169,7 @@
         UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
         UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
         kConfigurationDescriptorType << 8 | i, 0, header, header->size(),
-        kControlTransferTimeout,
+        kControlTransferTimeoutMs,
         base::Bind(&OnReadConfigDescriptorHeader, device_handle, desc_ptr, i,
                    closure));
   }
@@ -208,7 +208,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
       UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
       kStringDescriptorType << 8 | index, language_id, buffer, buffer->size(),
-      kControlTransferTimeout, base::Bind(&OnReadStringDescriptor, callback));
+      kControlTransferTimeoutMs, base::Bind(&OnReadStringDescriptor, callback));
 }
 
 void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle,
@@ -503,7 +503,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
       UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
       kDeviceDescriptorType << 8, 0, buffer, buffer->size(),
-      kControlTransferTimeout,
+      kControlTransferTimeoutMs,
       base::Bind(&OnReadDeviceDescriptor, device_handle, callback));
 }
 
diff --git a/device/usb/webusb_descriptors.cc b/device/usb/webusb_descriptors.cc
index e2ebc14..3df67eb 100644
--- a/device/usb/webusb_descriptors.cc
+++ b/device/usb/webusb_descriptors.cc
@@ -45,7 +45,7 @@
     0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
     0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
 
-const int kControlTransferTimeout = 60000;  // 1 minute
+const int kControlTransferTimeoutMs = 2000;  // 2 seconds
 
 using ReadWebUsbDescriptorsCallback =
     base::Callback<void(std::unique_ptr<WebUsbAllowedOrigins> allowed_origins,
@@ -218,7 +218,7 @@
   device_handle->ControlTransfer(
       UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
       UsbControlTransferRecipient::DEVICE, vendor_code, index, kGetUrlRequest,
-      buffer, buffer->size(), kControlTransferTimeout,
+      buffer, buffer->size(), kControlTransferTimeoutMs,
       base::Bind(&OnReadUrlDescriptor, url_map, index, callback));
 }
 
@@ -301,7 +301,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
       UsbControlTransferRecipient::DEVICE, vendor_code, 0,
       kGetAllowedOriginsRequest, new_buffer, new_buffer->size(),
-      kControlTransferTimeout,
+      kControlTransferTimeoutMs,
       base::Bind(&OnReadWebUsbAllowedOrigins, callback));
 }
 
@@ -314,7 +314,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
       UsbControlTransferRecipient::DEVICE, vendor_code, 0,
       kGetAllowedOriginsRequest, buffer, buffer->size(),
-      kControlTransferTimeout,
+      kControlTransferTimeoutMs,
       base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback,
                  vendor_code));
 }
@@ -361,7 +361,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
       UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
       kBosDescriptorType << 8, 0, new_buffer, new_buffer->size(),
-      kControlTransferTimeout,
+      kControlTransferTimeoutMs,
       base::Bind(&OnReadBosDescriptor, device_handle, callback));
 }
 
@@ -574,7 +574,7 @@
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
       UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
       kBosDescriptorType << 8, 0, buffer, buffer->size(),
-      kControlTransferTimeout,
+      kControlTransferTimeoutMs,
       base::Bind(&OnReadBosDescriptorHeader, device_handle, callback));
 }
 
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index db36c0f..a36f51b12 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -35,6 +35,7 @@
 #if defined(OS_WIN)
 #include <tchar.h>
 #include "ipc/handle_win.h"
+#include "ipc/ipc_platform_file.h"
 #endif
 
 namespace IPC {
@@ -842,6 +843,48 @@
 }
 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
 
+#if defined(OS_WIN)
+void ParamTraits<PlatformFileForTransit>::GetSize(base::PickleSizer* s,
+                                                  const param_type& p) {
+  GetParamSize(s, p.IsValid());
+  if (p.IsValid())
+    GetParamSize(s, p.GetHandle());
+}
+
+void ParamTraits<PlatformFileForTransit>::Write(base::Pickle* m,
+                                                const param_type& p) {
+  m->WriteBool(p.IsValid());
+  if (p.IsValid()) {
+    HandleWin handle_win(p.GetHandle(), HandleWin::DUPLICATE);
+    ParamTraits<HandleWin>::Write(m, handle_win);
+    ::CloseHandle(p.GetHandle());
+  }
+}
+
+bool ParamTraits<PlatformFileForTransit>::Read(const base::Pickle* m,
+                                               base::PickleIterator* iter,
+                                               param_type* r) {
+  bool is_valid;
+  if (!iter->ReadBool(&is_valid))
+    return false;
+  if (!is_valid) {
+    *r = PlatformFileForTransit();
+    return true;
+  }
+
+  HandleWin handle_win;
+  if (!ParamTraits<HandleWin>::Read(m, iter, &handle_win))
+    return false;
+  *r = PlatformFileForTransit(handle_win.get_handle());
+  return true;
+}
+
+void ParamTraits<PlatformFileForTransit>::Log(const param_type& p,
+                                              std::string* l) {
+  LogParam(p.GetHandle(), l);
+}
+#endif  // defined(OS_WIN)
+
 void ParamTraits<base::FilePath>::GetSize(base::PickleSizer* sizer,
                                           const param_type& p) {
   p.GetSizeForPickle(sizer);
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 847a617..dd6755e 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -49,6 +49,10 @@
 
 struct ChannelHandle;
 
+#if defined(OS_WIN)
+class PlatformFileForTransit;
+#endif
+
 // -----------------------------------------------------------------------------
 // How we send IPC message logs across channels.
 struct IPC_EXPORT LogData {
@@ -593,6 +597,19 @@
   static void Log(const param_type& p, std::string* l);
 };
 
+#if defined(OS_WIN)
+template <>
+struct IPC_EXPORT ParamTraits<PlatformFileForTransit> {
+  typedef PlatformFileForTransit param_type;
+  static void GetSize(base::PickleSizer* sizer, const param_type& p);
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r);
+  static void Log(const param_type& p, std::string* l);
+};
+#endif  // defined(OS_WIN)
+
 template <>
 struct IPC_EXPORT ParamTraits<base::FilePath> {
   typedef base::FilePath param_type;
diff --git a/ipc/ipc_platform_file.cc b/ipc/ipc_platform_file.cc
index dbcfddc..db5f8e5 100644
--- a/ipc/ipc_platform_file.cc
+++ b/ipc/ipc_platform_file.cc
@@ -11,6 +11,32 @@
 
 namespace IPC {
 
+#if defined(OS_WIN)
+PlatformFileForTransit::PlatformFileForTransit() : handle_(nullptr) {}
+
+PlatformFileForTransit::PlatformFileForTransit(HANDLE handle)
+    : handle_(handle) {}
+
+bool PlatformFileForTransit::operator==(
+    const PlatformFileForTransit& platform_file) const {
+  return handle_ == platform_file.handle_;
+}
+
+bool PlatformFileForTransit::operator!=(
+    const PlatformFileForTransit& platform_file) const {
+  return !(*this == platform_file);
+}
+
+HANDLE PlatformFileForTransit::GetHandle() const {
+  return handle_;
+}
+
+bool PlatformFileForTransit::IsValid() const {
+  return handle_ != nullptr;
+}
+
+#endif  // defined(OS_WIN)
+
 PlatformFileForTransit GetPlatformFileForTransit(base::PlatformFile handle,
                                                  bool close_source_handle) {
 #if defined(OS_WIN)
@@ -24,10 +50,7 @@
     return IPC::InvalidPlatformFileForTransit();
   }
 
-  IPC::PlatformFileForTransit out_handle = IPC::PlatformFileForTransit(
-      raw_handle, base::GetCurrentProcId());
-  out_handle.SetOwnershipPassesToIPC(true);
-  return out_handle;
+  return IPC::PlatformFileForTransit(raw_handle);
 #elif defined(OS_POSIX)
   // If asked to close the source, we can simply re-use the source fd instead of
   // dup()ing and close()ing.
diff --git a/ipc/ipc_platform_file.h b/ipc/ipc_platform_file.h
index 15807f6..ea295af8 100644
--- a/ipc/ipc_platform_file.h
+++ b/ipc/ipc_platform_file.h
@@ -14,17 +14,32 @@
 #include "base/file_descriptor_posix.h"
 #endif
 
-#if defined(OS_WIN)
-#include "base/memory/shared_memory_handle.h"
-#endif
-
 namespace IPC {
 
 #if defined(OS_WIN)
-// The semantics for IPC transfer of a SharedMemoryHandle are exactly the same
-// as for a PlatformFileForTransit. The object wraps a HANDLE, and has some
-// metadata that indicates the process to which the HANDLE belongs.
-using PlatformFileForTransit = base::SharedMemoryHandle;
+class IPC_EXPORT PlatformFileForTransit {
+ public:
+  // Creates an invalid platform file.
+  PlatformFileForTransit();
+
+  // Creates a platform file that takes unofficial ownership of |handle|. Note
+  // that ownership is not handled by a Scoped* class due to usage patterns of
+  // this class and its POSIX counterpart [base::FileDescriptor]. When this
+  // class is used as an input to an IPC message, the IPC subsystem will close
+  // |handle|. When this class is used as the output from an IPC message, the
+  // receiver is expected to take ownership of |handle|.
+  explicit PlatformFileForTransit(HANDLE handle);
+
+  // Comparison operators.
+  bool operator==(const PlatformFileForTransit& platform_file) const;
+  bool operator!=(const PlatformFileForTransit& platform_file) const;
+
+  HANDLE GetHandle() const;
+  bool IsValid() const;
+
+ private:
+  HANDLE handle_;
+};
 #elif defined(OS_POSIX)
 typedef base::FileDescriptor PlatformFileForTransit;
 #endif
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
index bf6f1bd9..80b233fb 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -533,13 +533,10 @@
         }
         builder.setFocusMode(jniFocusMode);
 
-        // Auto Exposure is the usual capability and state, unless AE is not available at all, which
-        // is signalled by the absence of Metering Areas. Exposure Compensation can also support or
-        // be locked, this is equivalent to AndroidMeteringMode.FIXED.
+        // Auto Exposure is understood to be supported always; besides that, only "locked"
+        // (equivalent to AndroidMeteringMode.FIXED) may be supported and/or configured.
         ArrayList<Integer> jniExposureModes = new ArrayList<Integer>(2);
-        if (parameters.getMaxNumMeteringAreas() > 0) {
-            jniExposureModes.add(AndroidMeteringMode.CONTINUOUS);
-        }
+        jniExposureModes.add(AndroidMeteringMode.CONTINUOUS);
         if (parameters.isAutoExposureLockSupported()) {
             jniExposureModes.add(AndroidMeteringMode.FIXED);
         }
diff --git a/mojo/edk/js/tests/OWNERS b/mojo/edk/js/tests/OWNERS
new file mode 100644
index 0000000..6c1f2fb
--- /dev/null
+++ b/mojo/edk/js/tests/OWNERS
@@ -0,0 +1,6 @@
+per-file *.mojom=file://mojo/OWNERS
+
+# These mojom test files don't really require security review, but we need to
+# add these to please PRESUBMIT.
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom
index 688b22b3..3e4bd1a2 100644
--- a/mojo/edk/js/tests/js_to_cpp.mojom
+++ b/mojo/edk/js/tests/js_to_cpp.mojom
@@ -28,6 +28,8 @@
   EchoArgs? item;
 };
 
+interface ForTesting {};
+
 // Note: For messages which control test flow, pick numbers that are unlikely
 // to be hit as a result of our deliberate corruption of response messages.
 interface CppSide {
@@ -40,7 +42,11 @@
   // Responses from specific tests.
   PingResponse();
   EchoResponse(EchoArgsList list);
-  BitFlipResponse(EchoArgsList arg);
+
+  // Having an associated interface pointer in the message makes sure the
+  // message header version 2 is tested.
+  BitFlipResponse(EchoArgsList arg, associated ForTesting? not_used);
+
   BackPointerResponse(EchoArgsList arg);
 };
 
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
index b6b74e31..d9fe22d 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.cc
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -239,7 +239,9 @@
     mishandled_messages_ += 1;
   }
 
-  void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+  void BitFlipResponse(
+      js_to_cpp::EchoArgsListPtr list,
+      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
     mishandled_messages_ += 1;
   }
 
@@ -332,7 +334,9 @@
   // js_to_cpp::CppSide:
   void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
 
-  void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+  void BitFlipResponse(
+      js_to_cpp::EchoArgsListPtr list,
+      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
     CheckCorruptedEchoArgsList(list);
   }
 
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
index 58592f8..6ffce09 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.js
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -114,7 +114,7 @@
       writeMessagePipe(messagePipe, sampleMessage);
       arg.message_handle = messagePipe.handle1;
 
-      this.cppSide_.bitFlipResponse(createEchoArgsList(arg));
+      this.cppSide_.bitFlipResponse(createEchoArgsList(arg), null);
 
       core.close(messagePipe.handle0);
       iteration += 1;
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index e5f3808..50d6d67 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -80,6 +80,7 @@
   if (version() < 2)
     return data() + header()->num_bytes;
 
+  DCHECK(!header_v2()->payload.is_null());
   return static_cast<const uint8_t*>(header_v2()->payload.Get());
 }
 
@@ -89,17 +90,14 @@
   if (version() < 2) {
     num_bytes = data_num_bytes() - header()->num_bytes;
   } else {
-    auto payload = reinterpret_cast<uintptr_t>(header_v2()->payload.Get());
-    if (!payload) {
-      num_bytes = 0;
-    } else {
-      auto payload_end =
-          reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get());
-      if (!payload_end)
-        payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes());
-      DCHECK_GE(payload_end, payload);
-      num_bytes = payload_end - payload;
-    }
+    auto payload_begin =
+        reinterpret_cast<uintptr_t>(header_v2()->payload.Get());
+    auto payload_end =
+        reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get());
+    if (!payload_end)
+      payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes());
+    DCHECK_GE(payload_end, payload_begin);
+    num_bytes = payload_end - payload_begin;
   }
   DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
   return static_cast<uint32_t>(num_bytes);
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc
index 9f8c6278..1f23256 100644
--- a/mojo/public/cpp/bindings/lib/message_header_validator.cc
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -73,9 +73,11 @@
   //   payload size).
   // - Validation of the payload contents will be done separately based on the
   //   payload type.
-  if (!header_v2->payload.is_null() &&
-      (!internal::ValidatePointer(header_v2->payload, validation_context) ||
-       !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) {
+  if (!internal::ValidatePointerNonNullable(header_v2->payload,
+                                            "null payload in message header",
+                                            validation_context) ||
+      !internal::ValidatePointer(header_v2->payload, validation_context) ||
+      !validation_context->ClaimMemory(header_v2->payload.Get(), 1)) {
     return false;
   }
 
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 0701f6a1..bdf9326 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -321,17 +321,9 @@
     CloseAllStreams(ERR_UNEXPECTED);
     DCHECK(observers_.empty());
     CloseAllObservers(ERR_UNEXPECTED);
+    CancelAllRequests(ERR_UNEXPECTED);
 
     connection()->set_debug_visitor(nullptr);
-
-    UMA_HISTOGRAM_COUNTS_1000("Net.QuicSession.AbortedPendingStreamRequests",
-                              stream_requests_.size());
-
-    while (!stream_requests_.empty()) {
-      StreamRequest* request = stream_requests_.front();
-      stream_requests_.pop_front();
-      request->OnRequestCompleteFailure(ERR_ABORTED);
-    }
   }
 
   if (connection()->connected()) {
@@ -1013,6 +1005,7 @@
   DCHECK(dynamic_streams().empty());
   CloseAllStreams(ERR_UNEXPECTED);
   CloseAllObservers(ERR_UNEXPECTED);
+  CancelAllRequests(ERR_CONNECTION_CLOSED);
   NotifyFactoryOfSessionClosedLater();
 }
 
@@ -1270,6 +1263,17 @@
   }
 }
 
+void QuicChromiumClientSession::CancelAllRequests(int net_error) {
+  UMA_HISTOGRAM_COUNTS_1000("Net.QuicSession.AbortedPendingStreamRequests",
+                            stream_requests_.size());
+
+  while (!stream_requests_.empty()) {
+    StreamRequest* request = stream_requests_.front();
+    stream_requests_.pop_front();
+    request->OnRequestCompleteFailure(net_error);
+  }
+}
+
 std::unique_ptr<base::Value> QuicChromiumClientSession::GetInfoAsValue(
     const std::set<HostPortPair>& aliases) {
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index a4c07d1..2622653 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -351,6 +351,7 @@
 
   void CloseAllStreams(int net_error);
   void CloseAllObservers(int net_error);
+  void CancelAllRequests(int net_error);
 
   // Notifies the factory that this session is going away and no more streams
   // should be created from it.  This needs to be called before closing any
diff --git a/net/quic/chromium/quic_chromium_client_session_test.cc b/net/quic/chromium/quic_chromium_client_session_test.cc
index ffae007..3d85ddd 100644
--- a/net/quic/chromium/quic_chromium_client_session_test.cc
+++ b/net/quic/chromium/quic_chromium_client_session_test.cc
@@ -318,6 +318,64 @@
   EXPECT_TRUE(quic_data.AllWriteDataConsumed());
 }
 
+TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) {
+  MockQuicData quic_data;
+  quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr));
+  quic_data.AddRead(server_maker_.MakeConnectionClosePacket(1));
+  quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  Initialize();
+  CompleteCryptoHandshake();
+
+  // Pump the message loop to read the connection close packet.
+  base::RunLoop().RunUntilIdle();
+
+  // Request a stream and verify that it failed.
+  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
+      session_->CreateStreamRequest();
+  TestCompletionCallback callback;
+  ASSERT_EQ(ERR_CONNECTION_CLOSED,
+            stream_request->StartRequest(callback.callback()));
+
+  EXPECT_TRUE(quic_data.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
+}
+
+TEST_P(QuicChromiumClientSessionTest, ConnectionCloseWithPendingStreamRequest) {
+  MockQuicData quic_data;
+  quic_data.AddWrite(client_maker_.MakeInitialSettingsPacket(1, nullptr));
+  quic_data.AddRead(ASYNC, ERR_IO_PENDING);
+  quic_data.AddRead(server_maker_.MakeConnectionClosePacket(1));
+  quic_data.AddSocketDataToFactory(&socket_factory_);
+
+  Initialize();
+  CompleteCryptoHandshake();
+
+  // Open the maximum number of streams so that a subsequent request
+  // can not proceed immediately.
+  const size_t kMaxOpenStreams = session_->max_open_outgoing_streams();
+  for (size_t i = 0; i < kMaxOpenStreams; i++) {
+    session_->CreateOutgoingDynamicStream(kDefaultPriority);
+  }
+  EXPECT_EQ(kMaxOpenStreams, session_->GetNumOpenOutgoingStreams());
+
+  // Request a stream and verify that it's pending.
+  std::unique_ptr<QuicChromiumClientSession::StreamRequest> stream_request =
+      session_->CreateStreamRequest();
+  TestCompletionCallback callback;
+  ASSERT_EQ(ERR_IO_PENDING, stream_request->StartRequest(callback.callback()));
+
+  // Close the connection and verify that the StreamRequest completes with
+  // an error.
+  quic_data.Resume();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));
+
+  EXPECT_TRUE(quic_data.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data.AllWriteDataConsumed());
+}
+
 TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) {
   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
   std::unique_ptr<QuicEncryptedPacket> settings_packet(
diff --git a/net/quic/core/crypto/quic_compressed_certs_cache.cc b/net/quic/core/crypto/quic_compressed_certs_cache.cc
index c9ba5301..84a58fa 100644
--- a/net/quic/core/crypto/quic_compressed_certs_cache.cc
+++ b/net/quic/core/crypto/quic_compressed_certs_cache.cc
@@ -19,7 +19,9 @@
 }  // namespace
 
 QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts()
-    : chain(nullptr) {}
+    : chain(nullptr),
+      client_common_set_hashes(nullptr),
+      client_cached_cert_hashes(nullptr) {}
 
 QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts(
     const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
diff --git a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
index da9344f..4fb5fe1 100644
--- a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
@@ -80,6 +80,7 @@
     perf_ui
     persistence
     platform
+    product_registry
     protocol
     sdk
     services
@@ -118,6 +119,7 @@
     perf_ui
     persistence
     platform
+    product_registry
     protocol
     quick_open
     sdk
@@ -159,6 +161,7 @@
     perf_ui
     persistence
     platform
+    product_registry
     protocol
     quick_open
     sdk
diff --git a/third_party/WebKit/LayoutTests/inspector/sha1.html b/third_party/WebKit/LayoutTests/inspector/sha1.html
index 8c0e2425..d018787 100644
--- a/third_party/WebKit/LayoutTests/inspector/sha1.html
+++ b/third_party/WebKit/LayoutTests/inspector/sha1.html
@@ -5,18 +5,18 @@
 <script>
 
 function initialize_ProductRegistry() {
-    InspectorTest.preloadModule('product_registry');
+    InspectorTest.preloadModule('product_registry_impl');
 }
 
 function test()
 {
-    InspectorTest.addResult('foobar : ' + ProductRegistry.sha1('foobar'));
-    InspectorTest.addResult('hello : ' + ProductRegistry.sha1('hello'));
-    InspectorTest.addResult('abcdefghijklmnopqrstuvwxyz : ' + ProductRegistry.sha1('abcdefghijklmnopqrstuvwxyz'));
-    InspectorTest.addResult('ABCDEFGHIJKLMNOPQRSTUVWXYZ : ' + ProductRegistry.sha1('ABCDEFGHIJKLMNOPQRSTUVWXYZ'));
-    InspectorTest.addResult('a : ' + ProductRegistry.sha1('a'));
-    InspectorTest.addResult('A : ' + ProductRegistry.sha1('A'));
-    InspectorTest.addResult('A1 : ' + ProductRegistry.sha1('A1'));
+    InspectorTest.addResult('foobar : ' + ProductRegistryImpl.sha1('foobar'));
+    InspectorTest.addResult('hello : ' + ProductRegistryImpl.sha1('hello'));
+    InspectorTest.addResult('abcdefghijklmnopqrstuvwxyz : ' + ProductRegistryImpl.sha1('abcdefghijklmnopqrstuvwxyz'));
+    InspectorTest.addResult('ABCDEFGHIJKLMNOPQRSTUVWXYZ : ' + ProductRegistryImpl.sha1('ABCDEFGHIJKLMNOPQRSTUVWXYZ'));
+    InspectorTest.addResult('a : ' + ProductRegistryImpl.sha1('a'));
+    InspectorTest.addResult('A : ' + ProductRegistryImpl.sha1('A'));
+    InspectorTest.addResult('A1 : ' + ProductRegistryImpl.sha1('A1'));
     InspectorTest.completeTest();
 }
 
diff --git a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
index 54d9e626..9a23b1c 100644
--- a/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
+++ b/third_party/WebKit/Source/core/dom/DOMImplementation.cpp
@@ -175,7 +175,7 @@
   if (mime_type.StartsWith("application/json", kTextCaseASCIIInsensitive))
     return true;
   if (mime_type.StartsWith("application/", kTextCaseASCIIInsensitive)) {
-    size_t subtype = mime_type.Find("+json", 12, kTextCaseASCIIInsensitive);
+    size_t subtype = mime_type.FindIgnoringASCIICase("+json", 12);
     if (subtype != kNotFound) {
       // Just check that a parameter wasn't matched.
       size_t parameter_marker = mime_type.Find(";");
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp
index 29e888e..59c3063 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserIdioms.cpp
@@ -358,7 +358,7 @@
   unsigned length = value.length();
 
   while (pos < length) {
-    pos = value.Find(kCharsetString, pos, kTextCaseASCIIInsensitive);
+    pos = value.FindIgnoringASCIICase(kCharsetString, pos);
     if (pos == kNotFound)
       break;
 
diff --git a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
index 0093c10..cc72791 100644
--- a/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
+++ b/third_party/WebKit/Source/core/html/parser/XSSAuditor.cpp
@@ -907,14 +907,12 @@
 bool XSSAuditor::IsContainedInRequest(const String& decoded_snippet) {
   if (decoded_snippet.IsEmpty())
     return false;
-  if (decoded_url_.Find(decoded_snippet, 0, kTextCaseUnicodeInsensitive) !=
-      kNotFound)
+  if (decoded_url_.FindIgnoringCase(decoded_snippet, 0) != kNotFound)
     return true;
   if (decoded_http_body_suffix_tree_ &&
       !decoded_http_body_suffix_tree_->MightContain(decoded_snippet))
     return false;
-  return decoded_http_body_.Find(decoded_snippet, 0,
-                                 kTextCaseUnicodeInsensitive) != kNotFound;
+  return decoded_http_body_.FindIgnoringCase(decoded_snippet, 0) != kNotFound;
 }
 
 bool XSSAuditor::IsLikelySafeResource(const String& url) {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
index a1eb1ff5..ed98379 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorDOMAgent.cpp
@@ -1009,14 +1009,13 @@
           AttributeCollection attributes = element->Attributes();
           for (auto& attribute : attributes) {
             // Add attribute pair
-            if (attribute.LocalName().Find(whitespace_trimmed_query, 0,
-                                           kTextCaseUnicodeInsensitive) !=
-                kNotFound) {
+            if (attribute.LocalName().FindIgnoringCase(whitespace_trimmed_query,
+                                                       0) != kNotFound) {
               result_collector.insert(node);
               break;
             }
-            size_t found_position = attribute.Value().Find(
-                attribute_query, 0, kTextCaseUnicodeInsensitive);
+            size_t found_position =
+                attribute.Value().FindIgnoringCase(attribute_query, 0);
             if (found_position != kNotFound) {
               if (!exact_attribute_match ||
                   (!found_position &&
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index c726bbd..bf835100 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -1810,7 +1810,9 @@
     // The following non-const functions for ObjectPaintProperties should only
     // be called from PaintPropertyTreeBuilder.
     ObjectPaintProperties& EnsurePaintProperties() {
-      return layout_object_.EnsureRarePaintData().EnsurePaintProperties();
+      return layout_object_.EnsureRarePaintData()
+          .EnsureFragment()
+          .EnsurePaintProperties();
     }
     ObjectPaintProperties* PaintProperties() {
       if (auto* paint_data = layout_object_.GetRarePaintData())
@@ -1818,8 +1820,10 @@
       return nullptr;
     }
     void ClearPaintProperties() {
-      if (auto* paint_data = layout_object_.GetRarePaintData())
-        paint_data->ClearPaintProperties();
+      if (auto* paint_data = layout_object_.GetRarePaintData()) {
+        if (auto* fragment = paint_data->Fragment())
+          fragment->ClearPaintProperties();
+      }
     }
 
     // The following non-const functions for local border box properties should
diff --git a/third_party/WebKit/Source/core/paint/BUILD.gn b/third_party/WebKit/Source/core/paint/BUILD.gn
index c5dad31..b460868 100644
--- a/third_party/WebKit/Source/core/paint/BUILD.gn
+++ b/third_party/WebKit/Source/core/paint/BUILD.gn
@@ -56,6 +56,8 @@
     "FirstMeaningfulPaintDetector.h",
     "FloatClipRecorder.cpp",
     "FloatClipRecorder.h",
+    "FragmentData.cpp",
+    "FragmentData.h",
     "FramePainter.cpp",
     "FramePainter.h",
     "FrameSetPainter.cpp",
diff --git a/third_party/WebKit/Source/core/paint/FragmentData.cpp b/third_party/WebKit/Source/core/paint/FragmentData.cpp
new file mode 100644
index 0000000..43e3e74
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/FragmentData.cpp
@@ -0,0 +1,20 @@
+// 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/paint/FragmentData.h"
+#include "core/paint/ObjectPaintProperties.h"
+
+namespace blink {
+
+ObjectPaintProperties& FragmentData::EnsurePaintProperties() {
+  if (!paint_properties_)
+    paint_properties_ = ObjectPaintProperties::Create();
+  return *paint_properties_.get();
+}
+
+void FragmentData::ClearPaintProperties() {
+  paint_properties_.reset(nullptr);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/FragmentData.h b/third_party/WebKit/Source/core/paint/FragmentData.h
new file mode 100644
index 0000000..8951c32
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/FragmentData.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FragmentData_h
+#define FragmentData_h
+
+#include "core/paint/ObjectPaintProperties.h"
+
+namespace blink {
+
+class ObjectPaintProperties;
+
+// Represents the data for a particular fragment of a LayoutObject.
+// Only LayoutObjects with a self-painting PaintLayer may have more than one
+// FragmentDeta, and even then only when they are inside of multicol.
+// See README.md.
+class CORE_EXPORT FragmentData {
+ public:
+  static std::unique_ptr<FragmentData> Create() {
+    return WTF::WrapUnique(new FragmentData());
+  }
+
+  ObjectPaintProperties* PaintProperties() const {
+    return paint_properties_.get();
+  }
+  ObjectPaintProperties& EnsurePaintProperties();
+  void ClearPaintProperties();
+
+  FragmentData* NextFragment() { return next_fragment_.get(); }
+
+ private:
+  // Holds references to the paint property nodes created by this object.
+  std::unique_ptr<ObjectPaintProperties> paint_properties_;
+
+  std::unique_ptr<FragmentData> next_fragment_;
+};
+
+}  // namespace blink
+
+#endif  // FragmentData_h
diff --git a/third_party/WebKit/Source/core/paint/README.md b/third_party/WebKit/Source/core/paint/README.md
index 34d5d57..b30e782 100644
--- a/third_party/WebKit/Source/core/paint/README.md
+++ b/third_party/WebKit/Source/core/paint/README.md
@@ -11,14 +11,14 @@
 
 ### Stacked elements and stacking contexts
 
-This chapter is basically a clarification of [CSS 2.1 appendix E. Elaborate description
-of Stacking Contexts](http://www.w3.org/TR/CSS21/zindex.html).
+This chapter is basically a clarification of [CSS 2.1 appendix E. Elaborate
+description of Stacking Contexts](http://www.w3.org/TR/CSS21/zindex.html).
 
-Note: we use 'element' instead of 'object' in this chapter to keep consistency with
-the spec. We use 'object' in other places in this document.
+Note: we use 'element' instead of 'object' in this chapter to keep consistency
+with the spec. We use 'object' in other places in this document.
 
-According to the documentation, we can have the following types of elements that are
-treated in different ways during painting:
+According to the documentation, we can have the following types of elements that
+are treated in different ways during painting:
 
 *   Stacked objects: objects that are z-ordered in stacking contexts, including:
 
@@ -30,27 +30,30 @@
         managed by real stacking contexts. They are positioned elements with
         `z-index: auto` (E.2.8 in the documentation).
 
-        They must be managed by the enclosing stacking context as stacked elements
-        because `z-index:auto` and `z-index:0` are considered equal for stacking
-        context sorting and they may interleave by DOM order.
+        They must be managed by the enclosing stacking context as stacked
+        elements because `z-index:auto` and `z-index:0` are considered equal for
+        stacking context sorting and they may interleave by DOM order.
 
-        The difference of a stacked element of this type from a real stacking context
-        is that it doesn't manage z-ordering of stacked descendants. These descendants
-        are managed by the parent stacking context of this stacked element.
+        The difference of a stacked element of this type from a real stacking 
+        context is that it doesn't manage z-ordering of stacked descendants.
+        These descendants are managed by the parent stacking context of this
+        stacked element.
 
-    "Stacked element" is not defined as a formal term in the documentation, but we found
-    it convenient to use this term to refer to any elements participating z-index ordering
-    in stacking contexts.
+    "Stacked element" is not defined as a formal term in the documentation, but
+    we found it convenient to use this term to refer to any elements
+    participating z-index ordering in stacking contexts.
 
-    A stacked element is represented by a `PaintLayerStackingNode` associated with a
-    `PaintLayer`. It's painted as self-painting `PaintLayer`s by `PaintLayerPainter`
-    by executing all of the steps of the painting algorithm explained in the documentation
-    for the element. When painting a stacked element of the second type, we don't
-    paint its stacked descendants which are managed by the parent stacking context.
+    A stacked element is represented by a `PaintLayerStackingNode` associated
+    with a `PaintLayer`. It's painted as self-painting `PaintLayer`s by
+    `PaintLayerPainter`
+    by executing all of the steps of the painting algorithm explained in the
+    documentation for the element. When painting a stacked element of the second
+    type, we don't paint its stacked descendants which are managed by the parent
+    stacking context.
 
-*   Non-stacked pseudo stacking contexts: elements that are not stacked, but paint
-    their descendants (excluding any stacked contents) as if they created stacking
-    contexts. This includes
+*   Non-stacked pseudo stacking contexts: elements that are not stacked, but
+    paint their descendants (excluding any stacked contents) as if they created
+    stacking contexts. This includes
 
     *   inline blocks, inline tables, inline-level replaced elements
         (E.2.7.2.1.4 in the documentation)
@@ -59,52 +62,52 @@
     *   [grid items](http://www.w3.org/TR/css-grid-1/#z-order)
     *   custom scrollbar parts
 
-    They are painted by `ObjectPainter::paintAllPhasesAtomically()` which executes
-    all of the steps of the painting algorithm explained in the documentation, except
-    ignores any descendants which are positioned or have non-auto z-index (which is
-    achieved by skipping descendants with self-painting layers).
+    They are painted by `ObjectPainter::paintAllPhasesAtomically()` which
+    executes all of the steps of the painting algorithm explained in the
+    documentation, except ignores any descendants which are positioned or have
+    non-auto z-index (which is achieved by skipping descendants with
+    self-painting layers).
 
 *   Other normal elements.
 
 ### Other glossaries
 
-*   Paint container: the parent of an object for painting, as defined by [CSS2.1 spec
-    for painting]((http://www.w3.org/TR/CSS21/zindex.html)). For regular objects,
-    this is the parent in the DOM. For stacked objects, it's the containing stacking
-    context-inducing object.
+*   Paint container: the parent of an object for painting, as defined by
+    [CSS2.1 spec for painting]((http://www.w3.org/TR/CSS21/zindex.html)). For
+    regular objects, this is the parent in the DOM. For stacked objects, it's
+    the containing stacking context-inducing object.
 
-*   Paint container chain: the chain of paint ancestors between an element and the
-    root of the page.
+*   Paint container chain: the chain of paint ancestors between an element and
+    the root of the page.
 
 *   Compositing container: an implementation detail of Blink, which uses
-    `PaintLayer`s to represent some layout objects. It is the ancestor along the paint
-    ancestor chain which has a PaintLayer. Implemented in
-    `PaintLayer::compositingContainer()`. Think of it as skipping intermediate normal
-    objects and going directly to the containing stacked object.
+    `PaintLayer`s to represent some layout objects. It is the ancestor along the
+    paint ancestor chain which has a PaintLayer. Implemented in
+    `PaintLayer::compositingContainer()`. Think of it as skipping intermediate
+    normal objects and going directly to the containing stacked object.
 
-*   Compositing container chain: same as paint chain, but for compositing container.
+*   Compositing container chain: same as paint chain, but for compositing
+    container.
 
-*   Paint invalidation container: the nearest object on the compositing container
-    chain which is composited.
+*   Paint invalidation container: the nearest object on the compositing
+    container chain which is composited.
 
 *   Visual rect: the bounding box of all pixels that will be painted by a
     display item client.
 
 ## Paint invalidation
 
-Paint invalidation marks anything that need to be painted differently from the original
-cached painting.
+Paint invalidation marks anything that need to be painted differently from the
+original cached painting.
 
 ### Slimming paint v1
 
-Though described in this document, most of the actual paint invalidation code is under
-`Source/core/layout`.
-
-Paint invalidation is a document cycle stage after compositing update and before paint.
-During the previous stages, objects are marked for needing paint invalidation checking
-if needed by style change, layout change, compositing change, etc. In paint invalidation stage,
-we traverse the layout tree in pre-order, crossing frame boundaries, for marked subtrees
-and objects and send the following information to `GraphicsLayer`s and `PaintController`s:
+Paint invalidation is a document cycle stage after compositing update and before
+paint. During the previous stages, objects are marked for needing paint
+invalidation checking if needed by style change, layout change, compositing
+change, etc. In paint invalidation stage, we traverse the layout tree in
+pre-order, crossing frame boundaries, for marked subtrees and objects and send
+the following information to `GraphicsLayer`s and `PaintController`s:
 
 *   invalidated display item clients: must invalidate all display item clients
     that will generate different display items.
@@ -115,163 +118,214 @@
 
 #### `PaintInvalidationState`
 
-`PaintInvalidationState` is an optimization used during the paint invalidation phase. Before
-the paint invalidation tree walk, a root `PaintInvalidationState` is created for the root
-`LayoutView`. During the tree walk, one `PaintInvalidationState` is created for each visited
-object based on the `PaintInvalidationState` passed from the parent object.
-It tracks the following information to provide O(1) complexity access to them if possible:
+`PaintInvalidationState` is an optimization used during the paint invalidation
+phase. Before the paint invalidation tree walk, a root `PaintInvalidationState`
+is created for the root `LayoutView`. During the tree walk, one
+`PaintInvalidationState` is created for each visited object based on the
+`PaintInvalidationState` passed from the parent object. It tracks the following
+information to provide O(1) complexity access to them if possible:
 
-*   Paint invalidation container: Since as indicated by the definitions in [Glossaries](#Other glossaries),
-    the paint invalidation container for stacked objects can differ from normal objects, we
-    have to track both separately. Here is an example:
+*   Paint invalidation container: Since as indicated by the definitions in
+    [Glossaries](#Other glossaries), the paint invalidation container for
+    stacked objects can differ from normal objects, we have to track both
+    separately. Here is an example:
 
         <div style="overflow: scroll">
             <div id=A style="position: absolute"></div>
             <div id=B></div>
         </div>
 
-    If the scroller is composited (for high-DPI screens for example), it is the paint invalidation
-    container for div B, but not A.
+    If the scroller is composited (for high-DPI screens for example), it is the
+    paint invalidation container for div B, but not A.
 
-*   Paint offset and clip rect: if possible, `PaintInvalidationState` accumulates paint offsets
-    and overflow clipping rects from the paint invalidation container to provide O(1) complexity to
-    map a point or a rect in current object's local space to paint invalidation container's space.
-    Because locations of objects are determined by their containing blocks, and the containing block
-    for absolute-position objects differs from non-absolute, we track paint offsets and overflow
-    clipping rects for absolute-position objects separately.
+*   Paint offset and clip rect: if possible, `PaintInvalidationState`
+    accumulates paint offsets and overflow clipping rects from the paint
+    invalidation container to provide O(1) complexity to map a point or a rect
+    in current object's local space to paint invalidation container's space.
+    Because locations of objects are determined by their containing blocks, and
+    the containing block for absolute-position objects differs from
+    non-absolute, we track paint offsets and overflow clipping rects for
+    absolute-position objects separately.
 
-In cases that accurate accumulation of paint offsets and clipping rects is impossible,
-we will fall back to slow-path using `LayoutObject::localToAncestorPoint()` or
-`LayoutObject::mapToVisualRectInAncestorSpace()`. This includes the following cases:
+In cases that accurate accumulation of paint offsets and clipping rects is
+impossible, we will fall back to slow-path using
+`LayoutObject::localToAncestorPoint()` or
+`LayoutObject::mapToVisualRectInAncestorSpace()`. This includes the following
+cases:
 
-*   An object has transform related property, is multi-column or has flipped blocks writing-mode,
-    causing we can't simply accumulate paint offset for mapping a local rect to paint invalidation
-    container;
+*   An object has transform related property, is multi-column or has flipped
+    blocks writing-mode, causing we can't simply accumulate paint offset for
+    mapping a local rect to paint invalidation container;
 
 *   An object has has filter (including filter induced by reflection), which
     needs to expand visual rect for descendants, because currently we don't
     include and filter extents into visual overflow;
 
-*   For a fixed-position object we calculate its offset using `LayoutObject::localToAncestorPoint()`,
-    but map for its descendants in fast-path if no other things prevent us from doing this;
+*   For a fixed-position object we calculate its offset using
+    `LayoutObject::localToAncestorPoint()`, but map for its descendants in
+    fast-path if no other things prevent us from doing this;
 
-*   Because we track paint offset from the normal paint invalidation container only, if we are going
-    to use `m_paintInvalidationContainerForStackedContents` and it's different from the normal paint
-    invalidation container, we have to force slow-path because the accumulated paint offset is not
-    usable;
+*   Because we track paint offset from the normal paint invalidation container
+    only, if we are going to use
+    `m_paintInvalidationContainerForStackedContents` and it's different from the
+    normal paint invalidation container, we have to force slow-path because the
+    accumulated paint offset is not usable;
 
-*   We also stop to track paint offset and clipping rect for absolute-position objects when
-    `m_paintInvalidationContainerForStackedContents` becomes different from `m_paintInvalidationContainer`.
+*   We also stop to track paint offset and clipping rect for absolute-position
+    objects when `m_paintInvalidationContainerForStackedContents` becomes
+    different from `m_paintInvalidationContainer`.
 
 ### Paint invalidation of texts
 
-Texts are painted by `InlineTextBoxPainter` using `InlineTextBox` as display item client.
-Text backgrounds and masks are painted by `InlineTextFlowPainter` using `InlineFlowBox`
-as display item client. We should invalidate these display item clients when their painting
-will change.
+Texts are painted by `InlineTextBoxPainter` using `InlineTextBox` as display
+item client. Text backgrounds and masks are painted by `InlineTextFlowPainter`
+using `InlineFlowBox` as display item client. We should invalidate these display
+item clients when their painting will change.
 
-`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if needed when
-new style is set on them. During paint invalidation, we invalidate the `InlineFlowBox`s
-directly contained by the `LayoutInline` in `LayoutInline::invalidateDisplayItemClients()` and
-`InlineTextBox`s contained by the `LayoutText` in `LayoutText::invalidateDisplayItemClients()`.
-We don't need to traverse into the subtree of `InlineFlowBox`s in `LayoutInline::invalidateDisplayItemClients()`
-because the descendant `InlineFlowBox`s and `InlineTextBox`s will be handled by their
-owning `LayoutInline`s and `LayoutText`s, respectively, when changed style is propagated.
+`LayoutInline`s and `LayoutText`s are marked for full paint invalidation if
+needed when new style is set on them. During paint invalidation, we invalidate
+the `InlineFlowBox`s directly contained by the `LayoutInline` in
+`LayoutInline::invalidateDisplayItemClients()` and `InlineTextBox`s contained by
+the `LayoutText` in `LayoutText::invalidateDisplayItemClients()`. We don't need
+to traverse into the subtree of `InlineFlowBox`s in
+`LayoutInline::invalidateDisplayItemClients()` because the descendant
+`InlineFlowBox`s and `InlineTextBox`s will be handled by their owning
+`LayoutInline`s and `LayoutText`s, respectively, when changed style is propagated.
 
 ### Specialty of `::first-line`
 
-`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the first line in the
-block having `::first-line` style. The actual applied style is computed from the `::first-line`
-style and other applicable styles.
+`::first-line` pseudo style dynamically applies to all `InlineBox`'s in the
+first line in the block having `::first-line` style. The actual applied style is
+computed from the `::first-line` style and other applicable styles.
 
-If the first line contains any `LayoutInline`, we compute the style from the `::first-line` style
-and the style of the `LayoutInline` and apply the computed style to the first line part of the
-`LayoutInline`. In blink's style implementation, the combined first line style of `LayoutInline`
-is identified with `FIRST_LINE_INHERITED` pseudo ID.
+If the first line contains any `LayoutInline`, we compute the style from the
+`::first-line` style and the style of the `LayoutInline` and apply the computed
+style to the first line part of the `LayoutInline`. In Blink's style
+implementation, the combined first line style of `LayoutInline` is identified
+with `FIRST_LINE_INHERITED` pseudo ID.
 
 The normal paint invalidation of texts doesn't work for first line because
-*   `ComputedStyle::visualInvalidationDiff()` can't detect first line style changes;
-*   The normal paint invalidation is based on whole LayoutObject's, not aware of the first line.
+*   `ComputedStyle::visualInvalidationDiff()` can't detect first line style
+    changes;
+*   The normal paint invalidation is based on whole LayoutObject's, not aware of
+    the first line.
 
-We have a special path for first line style change: the style system informs the layout system
-when the computed first-line style changes through `LayoutObject::firstLineStyleDidChange()`.
-When this happens, we invalidate all `InlineBox`es in the first line.
+We have a special path for first line style change: the style system informs the
+layout system when the computed first-line style changes through
+`LayoutObject::firstLineStyleDidChange()`. When this happens, we invalidate all
+`InlineBox`es in the first line.
 
 ### Slimming paint v2
 
 TODO(wangxianzhu): add details
 
-## [`PrePaintTreeWalk`](PrePaintTreeWalk.h) (Slimming paint v2 only)
+## [`PrePaintTreeWalk`](PrePaintTreeWalk.h) (Slimming Paint invalidation/v2 only)
 
-During `InPrePaint` document lifecycle state, this class is called to walk the whole
-layout tree, beginning from the root FrameView, across frame boundaries. We do the
-following during the tree walk:
+During `InPrePaint` document lifecycle state, this class is called to walk the
+whole layout tree, beginning from the root FrameView, across frame boundaries.
+We do the following during the tree walk:
 
-*   Building paint property tree: creates paint property tree nodes for special
-    things in the layout tree, including but not limit to: overflow clip, transform,
-    fixed-pos, animation, mask, filter, etc. Also sets direct compositing reasons to be
-    used later for compositing.
+### Building paint property trees
+[`PaintPropertyTreeBuilder`](PaintPropertyTreeBuilder.h)
 
-*   Paint invalidation: Not implemented yet. TODO(wangxianzhu): add details after
-    it's implemented.
+This class is responsible for building property trees
+(see [the platform paint README file](../../platform/graphics/paint/README.md)).
+
+Each `PaintLayer`'s `LayoutObject` has one or more `FragmentData` objects (see
+below for more on fragments). Every `FragmentData` has an
+`ObjectPaintProperties` object if any property nodes are induced by it. For
+example, if the object has a transform, its `ObjectPaintProperties::Transform()`
+field points at the `TransformPaintPropertyNode` representing that transform.
+
+The `NeedsPaintPropertyUpdate`, `SubtreeNeedsPaintPropertyUpdate` and
+`DescendantNeedsPaintPropertyUpdate` dirty bits on `LayoutObject` control how
+much of the layout tree is traversed during each `PrePaintTreeWalk`.
+
+### Fragments
+
+In the absence of multicolumn/pagination, there is a 1:1 correspondence between
+self-painting `PaintLayer`s and `FragmentData`. If there is
+multicolumn/pagination, there may be more `FragmentData`s.. If a `PaintLayer`
+has a property node, each of its fragments will have one. The parent of a
+fragment's property node is the property node that belongs to the ancestor
+`PaintLayer` which is part of the same column. For example, if there are 3
+columns and both a parent and child `PaintLayer` have a transform, there will be
+3 `FragmentData` objects for the parent, 3 for the child, each `FragmentData`
+will have its own `TransformPaintPropertyNode`, and the child's ith fragment's
+transform will point to the ith parent's transform.
+
+See [`LayoutMultiColumnFlowThread.h`](../layout/LayoutMultiColumnFlowThread.h)
+for a much more detail about multicolumn/pagination.
+
+###   Paint invalidation: `PaintInvalidator` implements a tree walk that
+performs paint invalidation. TODO(wangxianzhu): expand on this.
+
+### [`PaintPropertyTreeBuilder`](PaintPropertyTreeBuilder.h) (Slimming Paint invalidation only)
 
 ## Paint result caching
 
-`PaintController` holds the previous painting result as a cache of display items.
-If some painter would generate results same as those of the previous painting,
-we'll skip the painting and reuse the display items from cache.
+`PaintController` holds the previous painting result as a cache of display
+items. If some painter would generate results same as those of the previous
+painting, we'll skip the painting and reuse the display items from cache.
 
 ### Display item caching
 
-When a painter would create a `DrawingDisplayItem` exactly the same as the display item
-created in the previous painting, we'll reuse the previous one instead of repainting it.
+When a painter would create a `DrawingDisplayItem` exactly the same as the
+display item created in the previous painting, we'll reuse the previous one
+instead of repainting it.
 
 ### Subsequence caching
 
-When possible, we enclose the display items that `PaintLayerPainter::paintContents()` generates
-(including display items generated by sublayers) in a pair of `BeginSubsequence/EndSubsequence`
-display items.
+When possible, we enclose the display items that
+`PaintLayerPainter::paintContents()` generates (including display items
+generated by sublayers) in a pair of `BeginSubsequence/EndSubsequence` display
+items.
 
-In a subsequence paint, if the layer would generate exactly the same display items, we'll get
-the whole subsequence from the cache instead of repainting them.
+In a subsequence paint, if the layer would generate exactly the same display
+items, we'll get the whole subsequence from the cache instead of repainting
+them.
 
 There are many conditions affecting
 *   whether we need to generate subsequence for a PaintLayer;
 *   whether we can use cached subsequence for a PaintLayer.
-See `shouldCreateSubsequence()` and `shouldRepaintSubsequence()` in `PaintLayerPainter.cpp` for
-the conditions.
+See `shouldCreateSubsequence()` and `shouldRepaintSubsequence()` in
+`PaintLayerPainter.cpp` for the conditions.
 
 ## Empty paint phase optimization
 
-During painting, we walk the layout tree multiple times for multiple paint phases. Sometimes
-a layer contain nothing needing a certain paint phase and we can skip tree walk for such
-empty phases. Now we have optimized `PaintPhaseDescendantBlockBackgroundsOnly`,
-`PaintPhaseDescendantOutlinesOnly` and `PaintPhaseFloat` for empty paint phases.
+During painting, we walk the layout tree multiple times for multiple paint
+phases. Sometimes a layer contain nothing needing a certain paint phase and we
+can skip tree walk for such empty phases. Now we have optimized
+`PaintPhaseDescendantBlockBackgroundsOnly`, `PaintPhaseDescendantOutlinesOnly`
+and `PaintPhaseFloat` for empty paint phases.
 
-During paint invalidation, we set the containing self-painting layer's `needsPaintPhaseXXX`
-flag if the object has something needing to be painted in the paint phase.
+During paint invalidation, we set the containing self-painting layer's
+`needsPaintPhaseXXX` flag if the object has something needing to be painted in
+the paint phase.
 
-During painting, we check the flag before painting a paint phase and skip the tree walk if
-the flag is not set.
+During painting, we check the flag before painting a paint phase and skip the
+tree walk if the flag is not set.
 
-It's hard to clear a `needsPaintPhaseXXX` flag when a layer no longer needs the paint phase,
-so we never clear the flags. Instead, we use another set of flags (`previousPaintPhaseXXXWasEmpty`)
-to record if a painting of a phase actually produced nothing. We'll skip the next
-painting of the phase if the flag is set, regardless of the corresponding
-`needsPaintPhaseXXX` flag. We will clear the `previousPaintPhaseXXXWasEmpty` flags when
-we paint with different clipping, scroll offset or interest rect from the previous paint.
+It's hard to clear a `needsPaintPhaseXXX` flag when a layer no longer needs the
+paint phase, so we never clear the flags. Instead, we use another set of flags
+(`previousPaintPhaseXXXWasEmpty`) to record if a painting of a phase actually
+produced nothing. We'll skip the next painting of the phase if the flag is set,
+regardless of the corresponding `needsPaintPhaseXXX` flag. We will clear the
+`previousPaintPhaseXXXWasEmpty` flags when we paint with different clipping,
+scroll offset or interest rect from the previous paint.
 
-We don't clear the `previousPaintPhaseXXXWasEmpty` flags when the layer is marked `needsRepaint`.
-Instead we clear the flag when the corresponding `needsPaintPhaseXXX` is set. This ensures that
-we won't clear `previousPaintPhaseXXXWasEmpty` flags when unrelated things changed which won't
+We don't clear the `previousPaintPhaseXXXWasEmpty` flags when the layer is
+marked `needsRepaint`. Instead we clear the flag when the corresponding
+`needsPaintPhaseXXX` is set. This ensures that we won't clear
+`previousPaintPhaseXXXWasEmpty` flags when unrelated things changed which won't
 cause the paint phases to become non-empty.
 
-When layer structure changes, and we are not invalidate paint of the changed subtree,
-we need to manually update the `needsPaintPhaseXXX` flags. For example, if an object changes
-style and creates a self-painting-layer, we copy the flags from its containing self-painting
-layer to this layer, assuming that this layer needs all paint phases that its container
-self-painting layer needs.
+When layer structure changes, and we are not invalidate paint of the changed
+subtree, we need to manually update the `needsPaintPhaseXXX` flags. For example,
+if an object changes style and creates a self-painting-layer, we copy the flags
+from its containing self-painting layer to this layer, assuming that this layer
+needs all paint phases that its container self-painting layer needs.
 
-We could update the `needsPaintPhaseXXX` flags in a separate tree walk, but that would regress
-performance of the first paint. For slimming paint v2, we can update the flags during the
-pre-painting tree walk to simplify the logics.
+We could update the `needsPaintPhaseXXX` flags in a separate tree walk, but that
+would regress performance of the first paint. For slimming paint v2, we can
+update the flags during the pre-painting tree walk to simplify the logics.
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.cpp b/third_party/WebKit/Source/core/paint/RarePaintData.cpp
index 896be512..1a47a61 100644
--- a/third_party/WebKit/Source/core/paint/RarePaintData.cpp
+++ b/third_party/WebKit/Source/core/paint/RarePaintData.cpp
@@ -4,7 +4,7 @@
 
 #include "core/paint/RarePaintData.h"
 
-#include "core/paint/ObjectPaintProperties.h"
+#include "core/paint/FragmentData.h"
 #include "core/paint/PaintLayer.h"
 
 namespace blink {
@@ -17,14 +17,10 @@
   layer_ = std::move(layer);
 };
 
-ObjectPaintProperties& RarePaintData::EnsurePaintProperties() {
-  if (!paint_properties_)
-    paint_properties_ = ObjectPaintProperties::Create();
-  return *paint_properties_.get();
-}
-
-void RarePaintData::ClearPaintProperties() {
-  paint_properties_.reset(nullptr);
+FragmentData& RarePaintData::EnsureFragment() {
+  if (!fragment_data_)
+    fragment_data_ = FragmentData::Create();
+  return *fragment_data_.get();
 }
 
 void RarePaintData::ClearLocalBorderBoxProperties() {
@@ -41,13 +37,15 @@
 PropertyTreeState RarePaintData::ContentsProperties() const {
   DCHECK(local_border_box_properties_);
   PropertyTreeState contents(*local_border_box_properties_);
-  if (paint_properties_) {
-    if (paint_properties_->ScrollTranslation())
-      contents.SetTransform(paint_properties_->ScrollTranslation());
-    if (paint_properties_->OverflowClip())
-      contents.SetClip(paint_properties_->OverflowClip());
-    else if (paint_properties_->CssClip())
-      contents.SetClip(paint_properties_->CssClip());
+  if (fragment_data_) {
+    if (auto* properties = fragment_data_->PaintProperties()) {
+      if (properties->ScrollTranslation())
+        contents.SetTransform(properties->ScrollTranslation());
+      if (properties->OverflowClip())
+        contents.SetClip(properties->OverflowClip());
+      else if (properties->CssClip())
+        contents.SetClip(properties->CssClip());
+    }
   }
 
   // TODO(chrishtr): cssClipFixedPosition needs to be handled somehow.
diff --git a/third_party/WebKit/Source/core/paint/RarePaintData.h b/third_party/WebKit/Source/core/paint/RarePaintData.h
index a80ac6d..9353473 100644
--- a/third_party/WebKit/Source/core/paint/RarePaintData.h
+++ b/third_party/WebKit/Source/core/paint/RarePaintData.h
@@ -6,13 +6,13 @@
 #define RarePaintData_h
 
 #include "core/CoreExport.h"
+#include "core/paint/FragmentData.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Noncopyable.h"
 
 namespace blink {
 
 class PropertyTreeState;
-class ObjectPaintProperties;
 class PaintLayer;
 
 // This is for paint-related data on LayoutObject that is not needed on all
@@ -28,11 +28,15 @@
   PaintLayer* Layer() { return layer_.get(); }
   void SetLayer(std::unique_ptr<PaintLayer>);
 
+  FragmentData* Fragment() const { return fragment_data_.get(); }
+
+  FragmentData& EnsureFragment();
+
   ObjectPaintProperties* PaintProperties() const {
-    return paint_properties_.get();
+    if (fragment_data_)
+      return fragment_data_->PaintProperties();
+    return nullptr;
   }
-  ObjectPaintProperties& EnsurePaintProperties();
-  void ClearPaintProperties();
 
   PropertyTreeState* LocalBorderBoxProperties() const {
     return local_border_box_properties_.get();
@@ -52,8 +56,7 @@
   // depending on the return value of LayoutBoxModelObject::layerTypeRequired().
   std::unique_ptr<PaintLayer> layer_;
 
-  // Holds references to the paint property nodes created by this object.
-  std::unique_ptr<ObjectPaintProperties> paint_properties_;
+  std::unique_ptr<FragmentData> fragment_data_;
 
   // This is a complete set of property nodes that should be used as a
   // starting point to paint a LayoutObject. This data is cached because some
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 2edd250da..f350f94 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -313,6 +313,7 @@
   "front_end/network/networkLogView.css",
   "front_end/network/NetworkLogView.js",
   "front_end/network/NetworkLogViewColumns.js",
+  "front_end/network/NetworkGroupers.js",
   "front_end/network/networkManageCustomHeadersView.css",
   "front_end/network/NetworkManageCustomHeadersView.js",
   "front_end/network/NetworkOverview.js",
@@ -339,8 +340,6 @@
   "front_end/network_conditions/module.json",
   "front_end/network_conditions/NetworkConditionsSelector.js",
   "front_end/network_conditions/networkConditionsSettingsTab.css",
-  "front_end/network_group_lookup/module.json",
-  "front_end/network_group_lookup/NetworkProductGroupLookup.js",
   "front_end/network_log/HAREntry.js",
   "front_end/network_log/module.json",
   "front_end/network_log/NetworkLog.js",
@@ -378,9 +377,11 @@
   "front_end/platform/module.json",
   "front_end/platform/utilities.js",
   "front_end/product_registry/module.json",
-  "front_end/product_registry/ProductNameForURL.js",
-  "front_end/product_registry/ProductRegistryData.js",
-  "front_end/product_registry/sha1/sha1.js",
+  "front_end/product_registry/ProductRegistry.js",
+  "front_end/product_registry_impl/module.json",
+  "front_end/product_registry_impl/ProductRegistryImpl.js",
+  "front_end/product_registry_impl/ProductRegistryData.js",
+  "front_end/product_registry_impl/sha1/sha1.js",
   "front_end/profiler/BottomUpProfileDataGrid.js",
   "front_end/profiler/CPUProfileFlameChart.js",
   "front_end/profiler/CPUProfileView.js",
@@ -870,6 +871,7 @@
   "$resources_out_dir/network/network_module.js",
   "$resources_out_dir/object_ui/object_ui_module.js",
   "$resources_out_dir/perf_ui/perf_ui_module.js",
+  "$resources_out_dir/product_registry/product_registry_module.js",
   "$resources_out_dir/profiler/profiler_module.js",
   "$resources_out_dir/quick_open/quick_open_module.js",
   "$resources_out_dir/resources/resources_module.js",
@@ -892,8 +894,7 @@
   "$resources_out_dir/cm_modes/cm_modes_module.js",
   "$resources_out_dir/emulated_devices/emulated_devices_module.js",
   "$resources_out_dir/gonzales/gonzales_module.js",
-  "$resources_out_dir/network_group_lookup/network_group_lookup_module.js",
-  "$resources_out_dir/product_registry/product_registry_module.js",
+  "$resources_out_dir/product_registry_impl/product_registry_impl_module.js",
   "$resources_out_dir/screencast/screencast_module.js",
 ]
 
diff --git a/third_party/WebKit/Source/devtools/front_end/inspector.json b/third_party/WebKit/Source/devtools/front_end/inspector.json
index 6977ad6..597a0bf 100644
--- a/third_party/WebKit/Source/devtools/front_end/inspector.json
+++ b/third_party/WebKit/Source/devtools/front_end/inspector.json
@@ -17,11 +17,11 @@
         { "name": "services", "type": "autostart" },
         { "name": "elements", "condition": "!v8only" },
         { "name": "network", "condition": "!v8only" },
-        { "name": "network_group_lookup", "condition": "!v8only", "type": "remote" },
         { "name": "sources" },
         { "name": "timeline", "condition": "!v8only" },
         { "name": "timeline_model", "condition": "!v8only" },
-        { "name": "product_registry", "condition": "!v8only", "type": "remote" },
+        { "name": "product_registry", "condition": "!v8only" },
+        { "name": "product_registry_impl", "condition": "!v8only", "type": "remote" },
         { "name": "profiler" },
         { "name": "resources", "condition": "!v8only" },
         { "name": "audits", "condition": "!v8only" },
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
index a602a1f..5936f76e 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
@@ -38,14 +38,10 @@
   constructor(parentView) {
     super({});
     this._parentView = parentView;
-    /** @type {!Map<string, ?Network.NetworkColumnExtensionInterface>} */
-    this._columnExtensions = new Map();
     this._isHovered = false;
     this._showingInitiatorChain = false;
     /** @type {?SDK.NetworkRequest} */
     this._requestOrFirstKnownChildRequest = null;
-    /** @type {!Map<string, !UI.Icon>} */
-    this._columnIcons = new Map();
     /** @type {?Common.Color} */
     this._backgroundColor = null;
   }
@@ -88,14 +84,6 @@
     return /** @type {string} */ (color.asString(Common.Color.Format.HEX));
   }
 
-  /**
-   * @param {?Common.Color} color
-   */
-  setBackgroundColor(color) {
-    this._backgroundColor = color;
-    this._updateBackgroundColor();
-  }
-
   _updateBackgroundColor() {
     var element = this.existingElement();
     if (!element)
@@ -143,13 +131,6 @@
   }
 
   /**
-   * @param {!Map<string, ?Network.NetworkColumnExtensionInterface>} columnExtensions
-   */
-  setColumnExtensions(columnExtensions) {
-    this._columnExtensions = columnExtensions;
-  }
-
-  /**
    * @param {boolean} hovered
    * @param {boolean} showInitiatorChain
    */
@@ -351,6 +332,22 @@
   }
 
   /**
+   * @param {!ProductRegistry.Registry} productRegistry
+   * @param {!Network.NetworkNode} a
+   * @param {!Network.NetworkNode} b
+   * @return {number}
+   */
+  static ProductComparator(productRegistry, a, b) {
+    var aRequest = a.request();
+    var bRequest = b.request();
+    if (!aRequest || !bRequest)
+      return !aRequest ? -1 : 1;
+    var aName = productRegistry.nameForUrl(aRequest.parsedURL) || '';
+    var bName = productRegistry.nameForUrl(bRequest.parsedURL) || '';
+    return aName.localeCompare(bName) || aRequest.indentityCompare(bRequest);
+  }
+
+  /**
    * @param {!Network.NetworkNode} a
    * @param {!Network.NetworkNode} b
    * @return {number}
@@ -566,24 +563,6 @@
   }
 
   /**
-   * @param {!Map<string, ?Network.NetworkColumnExtensionInterface>} extensionsMap
-   * @param {string} extensionId
-   * @param {!Network.NetworkNode} a
-   * @param {!Network.NetworkNode} b
-   * @return {number}
-   */
-  static ExtensionColumnComparator(extensionsMap, extensionId, a, b) {
-    var aRequest = a.requestOrFirstKnownChildRequest();
-    var bRequest = b.requestOrFirstKnownChildRequest();
-    if (!aRequest || !bRequest)
-      return !aRequest ? -1 : 1;
-    var instance = extensionsMap.get(extensionId);
-    if (!instance)
-      return aRequest.indentityCompare(bRequest);
-    return instance.requestComparator(aRequest, bRequest);
-  }
-
-  /**
    * @override
    */
   showingInitiatorChainChanged() {
@@ -706,21 +685,11 @@
 
     element.classList.toggle('network-error-row', this._isFailed());
     element.classList.toggle('network-navigation-row', this._isNavigationRequest);
-    for (var rowDecorator of this._parentView.rowDecorators())
-      rowDecorator.decorate(this);
     super.createCells(element);
     this._updateBackgroundColor();
   }
 
   /**
-   * @param {string} columnId
-   * @param {!UI.Icon} icon
-   */
-  setIconForColumn(columnId, icon) {
-    this._columnIcons.set(columnId, icon);
-  }
-
-  /**
    * @param {!Element} element
    * @param {string} text
    */
@@ -736,21 +705,18 @@
    */
   createCell(columnIdentifier) {
     var cell = this.createTD(columnIdentifier);
-    var icon = this._columnIcons.get(columnIdentifier);
-    if (icon)
-      cell.appendChild(icon);
-    // If the key exists but the value is null it means the extension instance has not resolved yet.
-    // The view controller will force all rows to update when extension is resolved.
-    if (this._columnExtensions.has(columnIdentifier)) {
-      var instance = this._columnExtensions.get(columnIdentifier);
-      if (instance)
-        this._setTextAndTitle(cell, instance.lookupColumnValue(this._request));
-      return cell;
-    }
     switch (columnIdentifier) {
       case 'name':
         this._renderNameCell(cell);
         break;
+      case 'product':
+        if (!Runtime.experiments.isEnabled('networkGroupingRequests')) {
+          this._setTextAndTitle(cell, this._request.responseHeaderValue(columnIdentifier) || '');
+          break;
+        }
+        if (this.request())
+          ProductRegistry.instance().then(this._renderProductCell.bind(this, cell));
+        break;
       case 'method':
         this._setTextAndTitle(cell, this._request.requestMethod);
         break;
@@ -900,6 +866,42 @@
 
   /**
    * @param {!Element} cell
+   * @param {!ProductRegistry.Registry} productRegistry
+   */
+  _renderProductCell(cell, productRegistry) {
+    var request = this.request();
+    if (!request)
+      return;
+    var entry = productRegistry.entryForUrl(request.parsedURL);
+    if (!entry)
+      return;
+    this._setTextAndTitle(cell, entry.name);
+    if (entry.type !== null) {
+      var element = this.existingElement();
+      if (!element)
+        return;
+      switch (entry.type) {
+        case 0:
+          this._backgroundColor = Common.Color.fromRGBA([224, 247, 250, .6]);
+          cell.classList.add('product-ad');
+          break;
+        case 1:
+          this._backgroundColor = Common.Color.fromRGBA([255, 252, 225, .6]);
+          cell.classList.add('product-tracking');
+          break;
+        case 2:
+          this._backgroundColor = Common.Color.fromRGBA([211, 253, 211, .6]);
+          cell.classList.add('product-cdn');
+          break;
+        default:
+          this._backgroundColor = null;
+      }
+      this._updateBackgroundColor();
+    }
+  }
+
+  /**
+   * @param {!Element} cell
    */
   _renderStatusCell(cell) {
     cell.classList.toggle(
@@ -1100,8 +1102,6 @@
    */
   createCell(columnIdentifier) {
     var cell = this.createTD(columnIdentifier);
-    if (this._columnExtensions.has(columnIdentifier))
-      return cell;
     if (columnIdentifier === 'name') {
       var leftPadding = this.leftPadding ? this.leftPadding + 'px' : '';
       cell.style.setProperty('padding-left', leftPadding);
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js
new file mode 100644
index 0000000..3f78ea0
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkGroupers.js
@@ -0,0 +1,90 @@
+// 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.
+
+/**
+ * @implements {Network.GroupLookupInterface}
+ */
+Network.ProductGrouper = class {
+  constructor() {
+    /** @type {?ProductRegistry.Registry} */
+    this._productRegistry = null;
+  }
+
+  /**
+   * @override
+   * @return {!Promise}
+   */
+  initialize() {
+    return ProductRegistry.instance().then(productRegistry => this._productRegistry = productRegistry);
+  }
+
+  /**
+   * @override
+   * @param {!SDK.NetworkRequest} request
+   * @return {?*}
+   */
+  groupForRequest(request) {
+    if (!this._productRegistry)
+      return null;
+    var productName = this._productRegistry.nameForUrl(request.parsedURL);
+    if (!productName)
+      return null;
+    return productName;
+  }
+
+  /**
+   * @override
+   * @param {!*} key
+   * @return {string}
+   */
+  groupName(key) {
+    return /** @type {string} */ (key);
+  }
+};
+
+/**
+ * @implements {Network.GroupLookupInterface}
+ */
+Network.FrameGrouper = class {
+  constructor() {
+    /** @type {?ProductRegistry.Registry} */
+    this._productRegistry = null;
+  }
+
+  /**
+   * @override
+   * @return {!Promise}
+   */
+  initialize() {
+    return ProductRegistry.instance().then(productRegistry => this._productRegistry = productRegistry);
+  }
+
+  /**
+   * @override
+   * @param {!SDK.NetworkRequest} request
+   * @return {?*}
+   */
+  groupForRequest(request) {
+    var resourceTreeModel = request.networkManager().target().model(SDK.ResourceTreeModel);
+    if (!resourceTreeModel)
+      return null;
+    var frame = resourceTreeModel.frameForId(request.frameId);
+    if (!frame || frame.isMainFrame())
+      return null;
+    return frame;
+  }
+
+  /**
+   * @override
+   * @param {!*} frameArg
+   * @return {string}
+   */
+  groupName(frameArg) {
+    var frame = /** @type {!SDK.ResourceTreeFrame} */ (frameArg);
+    var entry = this._productRegistry ? this._productRegistry.entryForFrame(frame) : null;
+    if (entry)
+      return entry.name;
+    return (new Common.ParsedURL(frame.url)).host || frame.name || '<iframe>';
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
index 4ffbd80..9b9910b5 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -57,9 +57,6 @@
     this._durationCalculator = new Network.NetworkTransferDurationCalculator();
     this._calculator = this._timeCalculator;
 
-    /** @type {?Network.NetworkGroupLookupInterface} */
-    this._activeGroupLookup = null;
-
     /**
      * @this {Network.NetworkLogView}
      */
@@ -76,9 +73,6 @@
     this._nodesByRequestId = new Map();
     /** @type {!Map<*, !Network.NetworkGroupNode>} */
     this._nodeGroups = new Map();
-    /** @type {!Set<!Network.NetworkRowDecorator>} */
-    this._rowDecorators = new Set();
-
     /** @type {!Object.<string, boolean>} */
     this._staleRequestIds = {};
     /** @type {number} */
@@ -106,6 +100,14 @@
 
     this._headerHeight = 0;
 
+    /** @type {!Map<string, !Network.GroupLookupInterface>} */
+    this._groupLookups = new Map();
+    this._groupLookups.set('Product', new Network.ProductGrouper());
+    this._groupLookups.set('Frame', new Network.FrameGrouper());
+
+    /** @type {?Network.GroupLookupInterface} */
+    this._activeGroupLookup = null;
+
     this._addFilters();
     this._resetSuggestionBuilder();
     this._initializeView();
@@ -352,19 +354,29 @@
   }
 
   /**
-   * @param {?Network.NetworkGroupLookupInterface} grouping
+   * @return {!Map<string, !Network.GroupLookupInterface>}
    */
-  setGrouping(grouping) {
-    this._activeGroupLookup = grouping;
-    this._nodeGroups.clear();
-    this._invalidateAllItems();
+  groupLookups() {
+    return this._groupLookups;
   }
 
   /**
-   * @return {!Set<!Network.NetworkRowDecorator>}
+   * @param {string} groupKey
    */
-  rowDecorators() {
-    return this._rowDecorators;
+  setGrouping(groupKey) {
+    var groupLookup = this._groupLookups.get(groupKey) || null;
+    this._activeGroupLookup = groupLookup;
+    if (!groupLookup) {
+      this._nodeGroups.clear();
+      this._invalidateAllItems();
+      return;
+    }
+    groupLookup.initialize().then(() => {
+      if (this._activeGroupLookup !== groupLookup)
+        return;
+      this._nodeGroups.clear();
+      this._invalidateAllItems();
+    });
   }
 
   /**
@@ -498,12 +510,6 @@
     this.element.id = 'network-container';
     this._setupDataGrid();
 
-    self.runtime.allInstances(Network.NetworkRowDecorator).then(instances => {
-      for (var instance of instances)
-        this._rowDecorators.add(instance);
-      this._invalidateAllItems(true);
-    });
-
     this._columns.show(this.element);
 
     this._summaryBarElement = this.element.createChild('div', 'network-summary-bar');
@@ -901,7 +907,6 @@
     if (group)
       return group;
     group = new Network.NetworkGroupNode(this, this._activeGroupLookup.groupName(groupKey));
-    group.setColumnExtensions(this._columns.columnExtensions());
     this._nodeGroups.set(groupKey, group);
     return group;
   }
@@ -963,7 +968,6 @@
    */
   _appendRequest(request) {
     var node = new Network.NetworkRequestNode(this, request);
-    node.setColumnExtensions(this._columns.columnExtensions());
     node[Network.NetworkLogView._isFilteredOutSymbol] = true;
     node[Network.NetworkLogView._isMatchingSearchQuerySymbol] = false;
 
@@ -1820,30 +1824,23 @@
 /**
  * @interface
  */
-Network.NetworkGroupLookupInterface = function() {};
+Network.GroupLookupInterface = function() {};
 
-Network.NetworkGroupLookupInterface.prototype = {
+Network.GroupLookupInterface.prototype = {
+  /**
+   * @return {!Promise}
+   */
+  initialize: function() {},
+
   /**
    * @param {!SDK.NetworkRequest} request
    * @return {?*}
    */
-  groupForRequest(request) {},
+  groupForRequest: function(request) {},
 
   /**
    * @param {!*} key
    * @return {string}
    */
-  groupName(key) {}
-};
-
-/**
- * @interface
- */
-Network.NetworkRowDecorator = function() {};
-
-Network.NetworkRowDecorator.prototype = {
-  /**
-   * @param {!Network.NetworkNode} node
-   */
-  decorate(node) {}
+  groupName: function(key) {}
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index e206ff06..96d19c5a 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -29,12 +29,12 @@
     /** @type {!Array.<!Network.NetworkLogViewColumns.Descriptor>} */
     this._columns = [];
 
-    /** @type {!Map<string, ?Network.NetworkColumnExtensionInterface>} */
-    this._columnExtensions = new Map();
-
     this._waterfallRequestsAreStale = false;
     this._waterfallScrollerWidthIsStale = true;
 
+    /** @type {?ProductRegistry.Registry} */
+    this._productRegistryInstance = null;
+
     /** @type {!Components.Linkifier} */
     this._popupLinkifier = new Components.Linkifier();
 
@@ -78,6 +78,15 @@
 
   _setupDataGrid() {
     var defaultColumns = Network.NetworkLogViewColumns._defaultColumns;
+
+    if (Runtime.experiments.isEnabled('networkGroupingRequests')) {
+      defaultColumns.splice(1, 0, /** @type {!Network.NetworkLogViewColumns.Descriptor} */ ({
+                              id: 'product',
+                              title: Common.UIString('Product'),
+                              visible: true
+                            }));
+    }
+
     var defaultColumnConfig = Network.NetworkLogViewColumns._defaultColumnConfig;
 
     this._columns = /** @type {!Array<!Network.NetworkLogViewColumns.Descriptor>} */ ([]);
@@ -89,7 +98,6 @@
         columnConfig.titleDOMFragment = this._makeHeaderFragment(columnConfig.title, columnConfig.subtitle);
       this._columns.push(columnConfig);
     }
-    this._loadColumnExtensions();
     this._loadCustomColumnsAndSettings();
 
     this._popoverHelper = new UI.PopoverHelper(this._networkLogView.element, this._getPopoverRequest.bind(this));
@@ -279,6 +287,17 @@
       this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending());
       this._networkLogView.dataGridSorted();
       return;
+    } else if (columnId === 'product' && !this._productRegistryInstance) {
+      ProductRegistry.instance().then(productRegistry => {
+        this._productRegistryInstance = productRegistry;
+        var columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId);
+        if (!columnConfig)
+          return;
+        columnConfig.sortingFunction = Network.NetworkRequestNode.ProductComparator.bind(null, productRegistry);
+        if (this._dataGrid.sortColumnId() === 'product')
+          this._sortHandler();
+      });
+      return;
     }
     this._waterfallColumnSortIcon.setIconType('');
 
@@ -329,13 +348,6 @@
   }
 
   /**
-   * @return {!Map<string, ?Network.NetworkColumnExtensionInterface>}
-   */
-  columnExtensions() {
-    return this._columnExtensions;
-  }
-
-  /**
    * @param {!Network.NetworkLogViewColumns.Descriptor} columnConfig
    */
   _toggleColumnVisibility(columnConfig) {
@@ -353,43 +365,6 @@
     this._persistantSettings.set(saveableSettings);
   }
 
-  _loadColumnExtensions() {
-    var extensions = self.runtime.extensions(Network.NetworkColumnExtensionInterface);
-    for (var i = 0; i < extensions.length; i++) {
-      var extension = extensions[i];
-      var title = extension.title();
-      var columnId = title.toLowerCase() + '-extension';
-
-      this._columnExtensions.set(columnId, null);
-      extension.instance().then(extensionInstanceResolved.bind(this, columnId));
-
-      var columnConfig = /** @type {!Network.NetworkLogViewColumns.Descriptor} */ (
-          Object.assign({}, Network.NetworkLogViewColumns._defaultColumnConfig, {
-            id: columnId,
-            title: title,
-            isResponseHeader: false,
-            isCustomHeader: false,
-            visible: true,
-            sortingFunction:
-                Network.NetworkRequestNode.ExtensionColumnComparator.bind(null, this._columnExtensions, columnId)
-          }));
-      const columnIndex = i + 1;
-      this._columns.splice(columnIndex, 0, columnConfig);
-      if (this._dataGrid)
-        this._dataGrid.addColumn(Network.NetworkLogViewColumns._convertToDataGridDescriptor(columnConfig), columnIndex);
-    }
-
-    /**
-     * @param {string} columnId
-     * @param {!Network.NetworkColumnExtensionInterface} instance
-     * @this {Network.NetworkLogViewColumns}
-     */
-    function extensionInstanceResolved(columnId, instance) {
-      this._columnExtensions.set(columnId, instance);
-      this._networkLogView.columnExtensionResolved();
-    }
-  }
-
   _loadCustomColumnsAndSettings() {
     var savedSettings = this._persistantSettings.get();
     var columnIds = Object.keys(savedSettings);
@@ -642,6 +617,8 @@
 };
 
 Network.NetworkLogViewColumns._initialSortColumn = 'waterfall';
+/** @type {?ProductRegistry.Registry} */
+Network.NetworkRequestNode._productRegistryInstance = null;
 
 /**
  * @typedef {{
@@ -854,23 +831,3 @@
   Duration: 'duration',
   Latency: 'latency'
 };
-
-/**
- * @interface
- */
-Network.NetworkColumnExtensionInterface = function() {};
-
-Network.NetworkColumnExtensionInterface.prototype = {
-  /**
-   * @param {!SDK.NetworkRequest} request
-   * @return {string}
-   */
-  lookupColumnValue(request) {},
-
-  /**
-   * @param {!SDK.NetworkRequest} aRequest
-   * @param {!SDK.NetworkRequest} bRequest
-   * @return {number}
-   */
-  requestComparator(aRequest, bRequest) {}
-};
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
index d7b9ae8..a37bc9a3 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkPanel.js
@@ -40,8 +40,7 @@
     this._networkLogShowOverviewSetting = Common.settings.createSetting('networkLogShowOverview', true);
     this._networkLogLargeRowsSetting = Common.settings.createSetting('networkLogLargeRows', false);
     this._networkRecordFilmStripSetting = Common.settings.createSetting('networkRecordFilmStripSetting', false);
-    this._toggleRecordAction =
-        /** @type {!UI.Action }*/ (UI.actionRegistry.action('network.toggle-recording'));
+    this._toggleRecordAction = /** @type {!UI.Action }*/ (UI.actionRegistry.action('network.toggle-recording'));
 
     /** @type {?PerfUI.FilmStripView} */
     this._filmStripView = null;
@@ -89,8 +88,6 @@
     this._networkLogLargeRowsSetting.addChangeListener(this._toggleLargerRequests, this);
     this._networkRecordFilmStripSetting.addChangeListener(this._toggleRecordFilmStrip, this);
 
-    /** @type {!Map<string, !Runtime.Extension>} */
-    this._groupingExtensions = new Map();
     this._createToolbarButtons();
 
     this._toggleRecord(true);
@@ -188,35 +185,16 @@
   }
 
   _setupGroupingCombo() {
-    var extensions = self.runtime.extensions(Network.NetworkGroupLookupInterface);
-    if (!extensions.length)
+    if (!Runtime.experiments.isEnabled('networkGroupingRequests'))
       return;
-
-    var setting = Common.settings.createSetting('networkGrouping', '');
     /** @type {!Array<!{value: string, label: string, title: string}>} */
     var options = [{value: '', label: Common.UIString('No grouping'), title: Common.UIString('No grouping')}];
+    for (var name of this._networkLogView.groupLookups().keys())
+      options.push({value: name, label: Common.UIString(name), title: Common.UIString(name)});
 
-    extensions.forEach(extension => {
-      var identifier = extension.descriptor()['id'];
-      this._groupingExtensions.set(identifier, extension);
-      options.push({value: identifier, label: extension.title(), title: extension.title()});
-    });
+    var setting = Common.settings.createSetting('networkGrouping', '');
     this._panelToolbar.appendToolbarItem(new UI.ToolbarSettingComboBox(options, setting, Common.UIString('Group by')));
-    setting.addChangeListener(event => this._groupingChanged(/** @type {string} */ (event.data)));
-    this._groupingChanged(setting.get());
-  }
-
-  /**
-   * @param {string} identifier
-   */
-  _groupingChanged(identifier) {
-    var extension = this._groupingExtensions.get(identifier);
-    if (extension) {
-      extension.instance().then(
-          grouping => this._networkLogView.setGrouping(/** @type {?Network.NetworkGroupLookupInterface} */ (grouping)));
-    } else {
-      this._networkLogView.setGrouping(null);
-    }
+    setting.addChangeListener(event => this._networkLogView.setGrouping(/** @type {string} */ (event.data)));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/network/module.json b/third_party/WebKit/Source/devtools/front_end/network/module.json
index d8b62a36..6827c70 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/network/module.json
@@ -92,6 +92,20 @@
             "order": 40,
             "className": "Network.NetworkConfigView",
             "tags": "disk cache, network throttling, useragent, user agent"
+        },
+        {
+          "type": "@Network.GroupLookupInterface",
+          "className": "Network.ProductGrouper",
+          "experiment": "networkGroupingRequests",
+          "title": "Product",
+          "id": "product"
+        },
+        {
+          "type": "@Network.GroupLookupInterface",
+          "className": "Network.FrameGrouper",
+          "experiment": "networkGroupingRequests",
+          "title": "Frame",
+          "id": "frame"
         }
     ],
     "dependencies": [
@@ -102,7 +116,8 @@
         "data_grid",
         "network_conditions",
         "object_ui",
-        "network_log"
+        "network_log",
+        "product_registry"
     ],
     "scripts": [
         "BlockedURLsPane.js",
@@ -117,6 +132,7 @@
         "NetworkTimeCalculator.js",
         "NetworkLogView.js",
         "NetworkLogViewColumns.js",
+        "NetworkGroupers.js",
         "NetworkManageCustomHeadersView.js",
         "NetworkOverview.js",
         "NetworkWaterfallColumn.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
index 37842b76..e46ae42 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
+++ b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
@@ -161,6 +161,29 @@
 .network-header-subtitle {
     color: gray;
 }
+.network-log-grid.data-grid .product-ad::before,
+.network-log-grid.data-grid .product-tracking::before,
+.network-log-grid.data-grid .product-cdn::before {
+    content: "";
+    border-radius: 5px;
+    height: 5px;
+    width: 5px;
+    margin-right: 3px;
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.network-log-grid.data-grid .product-ad::before {
+    background-color: #00BCD4;
+}
+
+.network-log-grid.data-grid .product-tracking::before {
+    background-color: #FF9800;
+}
+
+.network-log-grid.data-grid .product-cdn::before {
+    background-color: #4CAF50;
+}
 
 .network-log-grid.data-grid.small .network-cell-subtitle,
 .network-log-grid.data-grid.small .network-header-subtitle {
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
deleted file mode 100644
index 1262dd9..0000000
--- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-/**
- * @implements {Network.NetworkColumnExtensionInterface}
- * @implements {Network.NetworkGroupLookupInterface}
- */
-NetworkGroupLookup.NetworkProductGroupLookup = class {
-  /**
-   * @override
-   * @param {!SDK.NetworkRequest} request
-   * @return {?*}
-   */
-  groupForRequest(request) {
-    var productName = ProductRegistry.nameForUrl(request.parsedURL);
-    if (!productName)
-      return null;
-    return productName;
-  }
-
-  /**
-   * @override
-   * @param {!*} key
-   * @return {string}
-   */
-  groupName(key) {
-    return /** @type {string} */ (key);
-  }
-
-  /**
-   * @override
-   * @param {!SDK.NetworkRequest} request
-   * @return {string}
-   */
-  lookupColumnValue(request) {
-    return ProductRegistry.nameForUrl(request.parsedURL) || '';
-  }
-
-  /**
-   * @override
-   * @param {!SDK.NetworkRequest} aRequest
-   * @param {!SDK.NetworkRequest} bRequest
-   * @return {number}
-   */
-  requestComparator(aRequest, bRequest) {
-    var aValue = this.lookupColumnValue(aRequest);
-    var bValue = this.lookupColumnValue(bRequest);
-    if (aValue === bValue)
-      return aRequest.indentityCompare(bRequest);
-    return aValue > bValue ? 1 : -1;
-  }
-};
-
-/**
- * @implements {Network.NetworkGroupLookupInterface}
- */
-NetworkGroupLookup.NetworkProductFrameGroupLookup = class {
-  /**
-   * @override
-   * @param {!SDK.NetworkRequest} request
-   * @return {?*}
-   */
-  groupForRequest(request) {
-    var resourceTreeModel = request.networkManager().target().model(SDK.ResourceTreeModel);
-    if (!resourceTreeModel)
-      return null;
-    var frame = resourceTreeModel.frameForId(request.frameId);
-    if (!frame || frame.isMainFrame())
-      return null;
-    return frame;
-  }
-
-  /**
-   * @override
-   * @param {!*} frameArg
-   * @return {string}
-   */
-  groupName(frameArg) {
-    var frame = /** @type {!SDK.ResourceTreeFrame} */ (frameArg);
-    var name;
-    var frameParsedURL = new Common.ParsedURL(frame.url);
-    if (frame.url)
-      name = ProductRegistry.nameForUrl(frameParsedURL);
-    if (name)
-      return name;
-    // We are not caching the frame url result because it may change.
-    var symbol = NetworkGroupLookup.NetworkProductFrameGroupLookup._productFrameGroupNameSymbol;
-    if (frame[symbol])
-      return frame[symbol];
-    frame[symbol] = this._lookupFrameStacktraceName(frame) || frameParsedURL.host || frame.name || '<iframe>';
-    return frame[symbol];
-  }
-
-  /**
-   * @param {!SDK.ResourceTreeFrame} frame
-   * @return {?string}
-   */
-  _lookupFrameStacktraceName(frame) {
-    // TODO(allada) This probably belongs in another shared module with some lookup that console will use for execution
-    // context name lookup.
-    var stackTrace = frame.creationStackTrace();
-    var name;
-    while (stackTrace) {
-      for (var stack of stackTrace.callFrames) {
-        if (stack.url)
-          name = ProductRegistry.nameForUrl(new Common.ParsedURL(stack.url));
-        if (name)
-          return name;
-      }
-      stackTrace = frame.parent;
-    }
-    return null;
-  }
-};
-
-/**
- * @implements {Network.NetworkRowDecorator}
- */
-NetworkGroupLookup.NetworkProductTypeGroupLookup = class {
-  /**
-   * @override
-   * @param {!Network.NetworkNode} node
-   */
-  decorate(node) {
-    var request = node.request();
-    var element = node.existingElement();
-    if (!request || !element)
-      return;
-    var typeName = ProductRegistry.typeForUrl(request.parsedURL);
-    if (typeName === null)
-      return;
-    var icon = UI.Icon.create('smallicon-network-product');
-    var color;
-    if (typeName === 1) {
-      color = Common.Color.fromRGBA([255, 252, 225, .6]);
-      icon.style.filter = 'hue-rotate(220deg) brightness(1.5)';
-    } else if (typeName === 2) {
-      color = Common.Color.fromRGBA([211, 253, 211, .6]);
-      icon.style.filter = 'hue-rotate(-90deg) brightness(1.5)';
-    } else {
-      color = Common.Color.fromRGBA([224, 247, 250, .6]);
-    }
-    node.setIconForColumn('product-extension', icon);
-    node.setBackgroundColor(color);
-  }
-};
-
-NetworkGroupLookup.NetworkProductFrameGroupLookup._productFrameGroupNameSymbol = Symbol('ProductFrameGroupName');
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json
deleted file mode 100644
index 9753bb9..0000000
--- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-    "extensions": [
-        {
-          "type": "@Network.NetworkGroupLookupInterface",
-          "className": "NetworkGroupLookup.NetworkProductGroupLookup",
-          "title": "Product",
-          "id": "product"
-        },
-        {
-          "type": "@Network.NetworkGroupLookupInterface",
-          "className": "NetworkGroupLookup.NetworkProductFrameGroupLookup",
-          "title": "Frame",
-          "id": "frame"
-        },
-        {
-          "type": "@Network.NetworkColumnExtensionInterface",
-          "className": "NetworkGroupLookup.NetworkProductGroupLookup",
-          "title": "Product"
-        },
-        {
-          "type": "@Network.NetworkRowDecorator",
-          "className": "NetworkGroupLookup.NetworkProductTypeGroupLookup"
-        }
-    ],
-    "dependencies": [
-        "network",
-        "product_registry",
-        "sdk"
-    ],
-    "experiment": "networkGroupingRequests",
-    "scripts": [
-        "NetworkProductGroupLookup.js"
-    ]
-}
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js
deleted file mode 100644
index 271157a..0000000
--- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-/**
- * @param {!Common.ParsedURL} parsedUrl
- * @return {?string}
- */
-ProductRegistry.nameForUrl = function(parsedUrl) {
-  var entry = ProductRegistry.entryForUrl(parsedUrl);
-  if (entry)
-    return entry.name;
-  return null;
-};
-
-/**
- * @param {!Common.ParsedURL} parsedUrl
- * @return {?ProductRegistry.ProductEntry}
- */
-ProductRegistry.entryForUrl = function(parsedUrl) {
-  if (parsedUrl.isDataURL())
-    return null;
-  // TODO(allada) This should be expanded to allow paths as as well as domain to find a product.
-  var productsByDomainHash = ProductRegistry._productsByDomainHash;
-  // Remove leading www. if it is the only subdomain.
-  var domain = parsedUrl.domain().replace(/^www\.(?=[^.]+\.[^.]+$)/, '');
-
-  var previousIndex = -1;
-  var index = -1;
-  // Ensure we loop with full domain first, but do not loop over last part (ie: ".com").
-  for (var nextIndex = domain.indexOf('.'); nextIndex !== -1; nextIndex = domain.indexOf('.', nextIndex + 1)) {
-    var previousSubdomain = domain.substring(previousIndex + 1, index);
-    var subDomain = domain.substring(index + 1);
-    var prefixes = productsByDomainHash.get(ProductRegistry._hashForDomain(subDomain));
-    previousIndex = index;
-    index = nextIndex;
-    if (!prefixes)
-      continue;
-    // Exact match domains are always highest priority.
-    if ('' in prefixes && domain === subDomain)
-      return prefixes[''];
-    if (previousSubdomain) {
-      for (var prefix in prefixes) {
-        var domainPrefix = previousSubdomain.substr(0, prefix.length);
-        if (domainPrefix === prefix && prefix !== '')
-          return prefixes[prefix];
-      }
-    }
-    // Process wildcard subdomain if no better match found.
-    if (prefixes && '*' in prefixes)
-      return prefixes['*'];
-  }
-  return null;
-};
-
-/**
- * @param {!Common.ParsedURL} parsedUrl
- * @return {?number}
- */
-ProductRegistry.typeForUrl = function(parsedUrl) {
-  var entry = ProductRegistry.entryForUrl(parsedUrl);
-  if (entry)
-    return entry.type;
-  return null;
-};
-
-/**
- * @param {string} domain
- * @return {string}
- */
-ProductRegistry._hashForDomain = function(domain) {
-  return ProductRegistry.sha1(domain).substr(0, 16);
-};
-
-/**
- * @param {!Array<string>} productNames
- * @param {!Array<!{hash: string, prefixes: !Object<string, !{product: number, type: (number|undefined)}>}>} data
- */
-ProductRegistry.register = function(productNames, data) {
-  for (var i = 0; i < data.length; i++) {
-    var entry = data[i];
-    var prefixes = {};
-    for (var prefix in entry.prefixes) {
-      var prefixEntry = entry.prefixes[prefix];
-      var type = prefixEntry.type !== undefined ? prefixEntry.type : null;
-      prefixes[prefix] = {name: productNames[prefixEntry.product], type: type};
-    }
-    ProductRegistry._productsByDomainHash.set(entry.hash, prefixes);
-  }
-};
-
-/** @typedef {!{name: string, type: ?number}} */
-ProductRegistry.ProductEntry;
-
-/** @type {!Map<string, !Object<string, !ProductRegistry.ProductEntry>>}} */
-ProductRegistry._productsByDomainHash = new Map();
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js
new file mode 100644
index 0000000..66f431c8
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistry.js
@@ -0,0 +1,43 @@
+// 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.
+/**
+ * @return {!Promise<!ProductRegistry.Registry>}
+ */
+ProductRegistry.instance = function() {
+  return self.runtime.extension(ProductRegistry.Registry).instance();
+};
+
+/**
+ * @interface
+ */
+ProductRegistry.Registry = function() {};
+
+ProductRegistry.Registry.prototype = {
+  /**
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?string}
+   */
+  nameForUrl: function(parsedUrl) {},
+
+  /**
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?ProductRegistry.Registry.ProductEntry}
+   */
+  entryForUrl: function(parsedUrl) {},
+
+  /**
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?number}
+   */
+  typeForUrl: function(parsedUrl) {},
+
+  /**
+   * @param {!SDK.ResourceTreeFrame} frame
+   * @return {?ProductRegistry.Registry.ProductEntry}
+   */
+  entryForFrame: function(frame) {}
+};
+
+/** @typedef {!{name: string, type: ?number}} */
+ProductRegistry.Registry.ProductEntry;
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/module.json b/third_party/WebKit/Source/devtools/front_end/product_registry/module.json
index e02f9bd..3f3ce5c 100644
--- a/third_party/WebKit/Source/devtools/front_end/product_registry/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry/module.json
@@ -2,11 +2,10 @@
     "extensions": [
     ],
     "dependencies": [
-        "common"
+        "common",
+        "sdk"
     ],
     "scripts": [
-        "ProductNameForURL.js",
-        "ProductRegistryData.js",
-        "sha1/sha1.js"
+        "ProductRegistry.js"
     ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js
similarity index 99%
rename from third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js
rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js
index ed6e469..4b93b182 100644
--- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryData.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 // clang-format off
 /* eslint-disable */
-ProductRegistry.register([
+ProductRegistryImpl.register([
   "1&1 Internet AG",
   "A9",
   "AdGenie",
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js
new file mode 100644
index 0000000..977720e5
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/ProductRegistryImpl.js
@@ -0,0 +1,143 @@
+// 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.
+
+/**
+ * @implements {ProductRegistry.Registry}
+ */
+ProductRegistryImpl.Registry = class {
+  constructor() {
+  }
+
+  /**
+   * @override
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?string}
+   */
+  nameForUrl(parsedUrl) {
+    var entry = this.entryForUrl(parsedUrl);
+    if (entry)
+      return entry.name;
+    return null;
+  }
+
+  /**
+   * @override
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?ProductRegistry.Registry.ProductEntry}
+   */
+  entryForUrl(parsedUrl) {
+    if (parsedUrl.isDataURL())
+      return null;
+    // TODO(allada) This should be expanded to allow paths as as well as domain to find a product.
+    var productsByDomainHash = ProductRegistryImpl._productsByDomainHash;
+    // Remove leading www. if it is the only subdomain.
+    var domain = parsedUrl.domain().replace(/^www\.(?=[^.]+\.[^.]+$)/, '');
+
+    var previousIndex = -1;
+    var index = -1;
+    // Ensure we loop with full domain first, but do not loop over last part (ie: ".com").
+    for (var nextIndex = domain.indexOf('.'); nextIndex !== -1; nextIndex = domain.indexOf('.', nextIndex + 1)) {
+      var previousSubdomain = domain.substring(previousIndex + 1, index);
+      var subDomain = domain.substring(index + 1);
+      var prefixes = productsByDomainHash.get(ProductRegistryImpl._hashForDomain(subDomain));
+      previousIndex = index;
+      index = nextIndex;
+      if (!prefixes)
+        continue;
+      // Exact match domains are always highest priority.
+      if ('' in prefixes && domain === subDomain)
+        return prefixes[''];
+      if (previousSubdomain) {
+        for (var prefix in prefixes) {
+          var domainPrefix = previousSubdomain.substr(0, prefix.length);
+          if (domainPrefix === prefix && prefix !== '')
+            return prefixes[prefix];
+        }
+      }
+      // Process wildcard subdomain if no better match found.
+      if (prefixes && '*' in prefixes)
+        return prefixes['*'];
+    }
+    return null;
+  }
+
+  /**
+   * @override
+   * @param {!Common.ParsedURL} parsedUrl
+   * @return {?number}
+   */
+  typeForUrl(parsedUrl) {
+    var entry = this.entryForUrl(parsedUrl);
+    if (entry)
+      return entry.type;
+    return null;
+  }
+
+  /**
+   * @override
+   * @param {!SDK.ResourceTreeFrame} frame
+   * @return {?ProductRegistry.Registry.ProductEntry}
+   */
+  entryForFrame(frame) {
+    var entry;
+    if (frame.url)
+      entry = this.entryForUrl(new Common.ParsedURL(frame.url));
+    if (entry)
+      return entry;
+    // We are not caching the frame url result because it may change.
+    var symbol = ProductRegistryImpl.Registry._productEntryForFrameSymbol;
+    if (!(symbol in frame))
+      frame[symbol] = this._lookupStackTraceEntryForFrame(frame);
+    return frame[symbol];
+  }
+
+  /**
+   * @param {!SDK.ResourceTreeFrame} frame
+   * @return {?ProductRegistry.Registry.ProductEntry}
+   */
+  _lookupStackTraceEntryForFrame(frame) {
+    var stackTrace = frame.creationStackTrace();
+    var entry;
+    while (stackTrace) {
+      for (var stack of stackTrace.callFrames) {
+        if (stack.url)
+          entry = this.entryForUrl(new Common.ParsedURL(stack.url));
+        if (entry)
+          return entry;
+      }
+      stackTrace = frame.parent;
+    }
+    return null;
+  }
+};
+
+ProductRegistryImpl.Registry._productEntryForFrameSymbol = Symbol('ProductEntryForFrame');
+
+/**
+ * @param {string} domain
+ * @return {string}
+ */
+ProductRegistryImpl._hashForDomain = function(domain) {
+  return ProductRegistryImpl.sha1(domain).substr(0, 16);
+};
+
+/**
+ * @param {!Array<string>} productNames
+ * @param {!Array<!{hash: string, prefixes: !Object<string, !{product: number, type: (number|undefined)}>}>} data
+ */
+ProductRegistryImpl.register = function(productNames, data) {
+  for (var i = 0; i < data.length; i++) {
+    var entry = data[i];
+    var prefixes = {};
+    for (var prefix in entry.prefixes) {
+      var prefixEntry = entry.prefixes[prefix];
+      var type = prefixEntry.type !== undefined ? prefixEntry.type : null;
+      prefixes[prefix] = {name: productNames[prefixEntry.product], type: type};
+    }
+    ProductRegistryImpl._productsByDomainHash.set(entry.hash, prefixes);
+  }
+};
+
+/** @type {!Map<string, !Object<string, !ProductRegistry.Registry.ProductEntry>>}} */
+ProductRegistryImpl._productsByDomainHash = new Map();
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json
new file mode 100644
index 0000000..39e1190
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/module.json
@@ -0,0 +1,18 @@
+{
+    "extensions": [
+        {
+          "type": "@ProductRegistry.Registry",
+          "className": "ProductRegistryImpl.Registry"
+        }
+    ],
+    "dependencies": [
+        "common",
+        "product_registry",
+        "sdk"
+    ],
+    "scripts": [
+        "ProductRegistryImpl.js",
+        "ProductRegistryData.js",
+        "sha1/sha1.js"
+    ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/README.chromium b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/README.chromium
similarity index 100%
rename from third_party/WebKit/Source/devtools/front_end/product_registry/sha1/README.chromium
rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/README.chromium
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js
similarity index 98%
rename from third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js
rename to third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js
index 5c7133e4..5385d0e 100644
--- a/third_party/WebKit/Source/devtools/front_end/product_registry/sha1/sha1.js
+++ b/third_party/WebKit/Source/devtools/front_end/product_registry_impl/sha1/sha1.js
@@ -12,7 +12,7 @@
  * @param {string} str
  * @return {string}
  */
-ProductRegistry.sha1 = function(str) {
+ProductRegistryImpl.sha1 = function(str) {
   return rstr2hex(rstr_sha1(str2rstr_utf8(str)));
 
   /**
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
index 083e595..0baaac0 100644
--- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
+++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -403,7 +403,7 @@
 }
 
 void ImageCapture::GetMediaTrackSettings(MediaTrackSettings& settings) const {
-  // Merge any present |m_settings| members into |settings|.
+  // Merge any present |settings_| members into |settings|.
 
   if (settings_.hasWhiteBalanceMode())
     settings.setWhiteBalanceMode(settings_.whiteBalanceMode());
@@ -412,8 +412,10 @@
   if (settings_.hasFocusMode())
     settings.setFocusMode(settings_.focusMode());
 
-  if (settings_.hasPointsOfInterest())
+  if (settings_.hasPointsOfInterest() &&
+      !settings_.pointsOfInterest().IsEmpty()) {
     settings.setPointsOfInterest(settings_.pointsOfInterest());
+  }
 
   if (settings_.hasExposureCompensation())
     settings.setExposureCompensation(settings_.exposureCompensation());
@@ -504,7 +506,9 @@
       caps->SetImageWidth(
           MediaSettingsRange::Create(std::move(capabilities->width)));
     }
-    caps->SetFillLightMode(capabilities->fill_light_mode);
+
+    if (!capabilities->fill_light_mode.IsEmpty())
+      caps->SetFillLightMode(capabilities->fill_light_mode);
 
     resolver->Resolve(caps);
   }
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
index 52fb12ca..9721bf6c 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
@@ -305,8 +305,7 @@
       ++pos;
     SkipWhiteSpace(refresh, pos, matcher);
     unsigned url_start_pos = pos;
-    if (refresh.Find("url", url_start_pos, kTextCaseASCIIInsensitive) ==
-        url_start_pos) {
+    if (refresh.FindIgnoringASCIICase("url", url_start_pos) == url_start_pos) {
       url_start_pos += 3;
       SkipWhiteSpace(refresh, url_start_pos, matcher);
       if (refresh[url_start_pos] == '=') {
@@ -406,7 +405,7 @@
   unsigned length = media_type.length();
 
   while (pos < length) {
-    pos = media_type.Find("charset", pos, kTextCaseASCIIInsensitive);
+    pos = media_type.FindIgnoringASCIICase("charset", pos);
     if (pos == kNotFound || !pos) {
       charset_len = 0;
       return;
diff --git a/third_party/WebKit/Source/platform/network/mime/ContentType.cpp b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
index b02c9819..b34d1a5d 100644
--- a/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
+++ b/third_party/WebKit/Source/platform/network/mime/ContentType.cpp
@@ -41,7 +41,7 @@
   size_t semi = stripped_type.find(';');
   if (semi != kNotFound) {
     size_t start =
-        stripped_type.Find(parameter_name, semi + 1, kTextCaseASCIIInsensitive);
+        stripped_type.FindIgnoringASCIICase(parameter_name, semi + 1);
     if (start != kNotFound) {
       start = stripped_type.find('=', start + parameter_name.length());
       if (start != kNotFound) {
diff --git a/third_party/android_platform/development/scripts/stack b/third_party/android_platform/development/scripts/stack
index 010488c6..8d09461 100755
--- a/third_party/android_platform/development/scripts/stack
+++ b/third_party/android_platform/development/scripts/stack
@@ -145,6 +145,7 @@
                                         "output-directory=",
                                         "symbols-dir=",
                                         "symbols-zip=",
+                                        "packed-lib=",
                                         "arch=",
                                         "fallback-monochrome",
                                         "verbose",
@@ -154,9 +155,9 @@
 
   zip_arg = None
   more_info = False
-  packed_relocation_adjustments = "unknown"
   fallback_monochrome = False
   arch_defined = False
+  packed_libs = []
   for option, value in options:
     if option == "--help":
       PrintUsage()
@@ -171,10 +172,8 @@
       symbol.CHROME_SYMBOLS_DIR = os.path.join(symbol.CHROME_SRC, value)
     elif option == "--output-directory":
       constants.SetOutputDirectory(value)
-    elif option == "--packed-relocation-adjustments":
-      packed_relocation_adjustments = True
-    elif option == "--no-packed-relocation-adjustments":
-      packed_relocation_adjustments = False
+    elif option == "--packed-lib":
+      packed_libs.append(os.path.expanduser(value))
     elif option == "--more-info":
       more_info = True
     elif option == "--less-info":
@@ -183,6 +182,11 @@
       fallback_monochrome = True
     elif option == "--verbose":
       logging.basicConfig(level=logging.DEBUG)
+    elif option in (
+        '--packed-relocation-adjustments',
+        '--no-packed-relocation-adjustments'):
+      print ('--[no-]packed-relocation-adjustments options are deprecated. '
+             'Specify packed libs directory instead.')
 
   if len(arguments) > 1:
     PrintUsage()
@@ -205,25 +209,16 @@
   if zip_arg:
     rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)
 
-  if packed_relocation_adjustments == "unknown":
-    version = stack_libs.GetTargetAndroidVersionNumber(lines)
-    if version == None:
-      packed_relocation_adjustments = False
-      print ("Unknown Android release, "
-             + "consider --[no-]packed-relocation-adjustments options")
-    elif version >= _ANDROID_M_MAJOR_VERSION:
-      packed_relocation_adjustments = False
-    else:
-      packed_relocation_adjustments = True
-      print ("Pre-M Android release detected, "
-             + "added --packed-relocation-adjustments option")
-  else:
-    packed_relocation_adjustments = False
+  version = stack_libs.GetTargetAndroidVersionNumber(lines)
+  if version is None:
+    print ("Unknown Android release, "
+           "consider passing --packed-lib.")
+  elif version < _ANDROID_M_MAJOR_VERSION and not packed_libs:
+    print ("Pre-M Android release detected, "
+           "but --packed-lib not specified. Stack symbolization may fail.")
 
-  if packed_relocation_adjustments:
-    constants.CheckOutputDirectory()
-    stripped_libs_dir = constants.GetOutDirectory()
-    load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs_dir)
+  if (version is None or version < _ANDROID_M_MAJOR_VERSION) and packed_libs:
+    load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs=packed_libs)
   else:
     load_vaddrs = {}
 
diff --git a/third_party/android_platform/development/scripts/stack_libs.py b/third_party/android_platform/development/scripts/stack_libs.py
index 6cc1a86..8f8dd423 100755
--- a/third_party/android_platform/development/scripts/stack_libs.py
+++ b/third_party/android_platform/development/scripts/stack_libs.py
@@ -9,12 +9,15 @@
 
 import glob
 import os.path
+import re
 import subprocess
 
 
 _BASE_APK = 'base.apk'
 _LIBCHROME_SO = 'libchrome.so'
 
+_BUILD_FINGERPRINT_RE = re.compile('.*Build fingerprint: (.*)$')
+
 
 def GetTargetAndroidVersionNumber(lines):
   """Return the Android major version number from the build fingerprint.
@@ -26,13 +29,15 @@
   """
   # For example, "Build fingerprint: 'Android/aosp_flo/flo:5.1.1/...'" is 5.
   for line in lines:
-    if line.startswith('Build fingerprint: '):
-      fingerprint = line.split()[2]
+    m = _BUILD_FINGERPRINT_RE.match(line)
+    if not m:
+      continue
+    fingerprint = m.group(1)
+    try:
       version = fingerprint.split('/')[2].split(':')[1].split('.')[0]
-      try:
-        return int(version)
-      except ValueError:
-        return None
+      return int(version)
+    except Exception:
+      pass
   return None
 
 
@@ -90,7 +95,7 @@
   return 0
 
 
-def GetLoadVaddrs(stripped_libs_dir):
+def GetLoadVaddrs(stripped_libs=None, stripped_libs_dir=None):
   """Return a dict of minimum VirtAddr for libraries in the given directory.
 
   The dictionary returned may be passed to stack_core.ConvertTrace(). In
@@ -103,8 +108,11 @@
   Returns:
     {'libchrome.so': 12345, ...}
   """
-  libs = glob.glob(os.path.join(stripped_libs_dir, '*.so'))
-  libs = [l for l in libs if _HasElfHeader(l)]
+  if not stripped_libs:
+    stripped_libs = []
+  if stripped_libs_dir:
+    stripped_libs.extend(glob.glob(os.path.join(stripped_libs_dir, '*.so')))
+  libs = [l for l in stripped_libs if _HasElfHeader(l)]
 
   load_vaddrs = {}
   for lib in libs:
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 233d600..943e801 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -103577,6 +103577,7 @@
   <int value="-909641013" label="DataReductionProxySiteBreakdown:enabled"/>
   <int value="-908421850" label="PointerEvent:enabled"/>
   <int value="-907234795" label="NewAudioRenderingMixingStrategy:disabled"/>
+  <int value="-899393472" label="enable-new-app-menu-icon"/>
   <int value="-899334103" label="disable-fast-text-autosizing"/>
   <int value="-898594349" label="ash-enable-stable-overview-order"/>
   <int value="-898499262" label="ImprovedA2HS:enabled"/>