diff --git a/AUTHORS b/AUTHORS
index 233d94eb..450bf6b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -367,6 +367,7 @@
 Jungkee Song <jungkee.song@samsung.com>
 Junmin Zhu <junmin.zhu@intel.com>
 Kai Jiang <jiangkai@gmail.com>
+Kai Köhne <kai.koehne@qt.io>
 Kal Conley <kcconley@gmail.com>
 Kalyan Kondapally <kalyan.kondapally@intel.com>
 Kamil Jiwa <kamil.jiwa@gmail.com>
diff --git a/DEPS b/DEPS
index 30c001f..7dbbd90 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': '2103cf0ff09763aeaa35508734f765aec9b75665',
+  'skia_revision': '6d4d6cce5e6e73d14c5d39c20321b189d737dfd5',
   # 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': '4b8a39354acd5949f3a389581fe6bda679a4e6d2',
+  'v8_revision': 'd6a08d86c04b5b652533978ef6b257de09d0599e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -228,7 +228,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '3e52797e71d77b2d61bb31bf035314c030d0cfcc', # commit position 16104
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '7367842b0870f9b03b9559c584899d4bf0ad93ec', # commit position 16120
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index dce61ad..9f60262c 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -76,6 +76,8 @@
 #if !defined(MAC_OS_X_VERSION_10_10) || \
     MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
 BASE_EXPORT extern NSString* const NSUserActivityTypeBrowsingWeb;
+BASE_EXPORT extern NSString* const NSAppearanceNameVibrantDark;
+BASE_EXPORT extern NSString* const NSAppearanceNameVibrantLight;
 #endif  // MAC_OS_X_VERSION_10_10
 }  // extern "C"
 
@@ -108,6 +110,10 @@
 - (void)viewDidLoad;
 @end
 
+@interface NSWindow (YosemiteSDK)
+- (void)setTitlebarAppearsTransparent:(BOOL)flag;
+@end
+
 @interface NSProcessInfo (YosemiteSDK)
 @property(readonly) NSOperatingSystemVersion operatingSystemVersion;
 @end
@@ -116,6 +122,12 @@
 @property(getter=isActive) BOOL active;
 @end
 
+@interface NSVisualEffectView (YosemiteSDK)
+- (void)setState:(NSVisualEffectState)state;
+@end
+
+@class NSVisualEffectView;
+
 #endif  // MAC_OS_X_VERSION_10_10
 
 // Once Chrome no longer supports OSX 10.10.2, everything within this
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 3a529af..98d1d7d 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -342,6 +342,7 @@
       'android_resources': ['build_config', 'resources_zip'],
       'android_apk': ['build_config', 'jar_path', 'dex_path', 'resources_zip'],
       'deps_dex': ['build_config', 'dex_path'],
+      'dist_jar': ['build_config'],
       'resource_rewriter': ['build_config'],
       'group': ['build_config'],
   }
@@ -456,10 +457,10 @@
       deps_info['incremental_install_script_path'] = (
           options.incremental_install_script_path)
 
+  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
     # Classpath values filled in below (after applying tested_apk_config).
     config['javac'] = {}
 
-
   if options.type in ('java_binary', 'java_library'):
     # Only resources might have srcjars (normal srcjar targets are listed in
     # srcjar_deps). A resource's srcjar contains the R.java file for those
@@ -548,7 +549,7 @@
   if options.type in ['android_apk', 'deps_dex']:
     deps_dex_files = [c['dex_path'] for c in all_library_deps]
 
-  if options.type in ('java_binary', 'java_library', 'android_apk'):
+  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
     javac_classpath = [c['jar_path'] for c in direct_library_deps]
     java_full_classpath = [c['jar_path'] for c in all_library_deps]
 
@@ -627,7 +628,7 @@
     dex_config = config['final_dex']
     dex_config['dependency_dex_files'] = deps_dex_files
 
-  if options.type in ('java_binary', 'java_library', 'android_apk'):
+  if options.type in ('java_binary', 'java_library', 'android_apk', 'dist_jar'):
     config['javac']['classpath'] = javac_classpath
     config['javac']['interface_classpath'] = [
         _AsInterfaceJar(p) for p in javac_classpath]
@@ -635,14 +636,19 @@
       'full_classpath': java_full_classpath
     }
 
-  if options.type == 'android_apk':
+  if options.type in ('android_apk', 'dist_jar'):
     dependency_jars = [c['jar_path'] for c in all_library_deps]
-    all_interface_jars = [
-        _AsInterfaceJar(p) for p in dependency_jars + [options.jar_path]]
+    all_interface_jars = [_AsInterfaceJar(p) for p in dependency_jars]
+    if options.type == 'android_apk':
+      all_interface_jars.append(_AsInterfaceJar(options.jar_path))
+
     config['dist_jar'] = {
       'dependency_jars': dependency_jars,
       'all_interface_jars': all_interface_jars,
     }
+
+  if options.type == 'android_apk':
+    dependency_jars = [c['jar_path'] for c in all_library_deps]
     manifest = AndroidManifest(options.android_manifest)
     deps_info['package_name'] = manifest.GetPackageName()
     if not options.tested_apk_config and manifest.GetInstrumentation():
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 102e98c..32a00a1 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -59,7 +59,7 @@
   # Don't need to enforce naming scheme for these targets since we never
   # consider them in dependency chains.
   if (!_is_prebuilt_binary && type != "android_apk" && type != "java_binary" &&
-      type != "resource_rewriter") {
+      type != "resource_rewriter" && type != "dist_jar") {
     set_sources_assignment_filter(_java_target_whitelist)
     _parent_invoker = invoker.invoker
     _target_label =
@@ -86,8 +86,9 @@
 
     assert(type == "android_apk" || type == "java_library" ||
            type == "android_resources" || type == "deps_dex" ||
-           type == "android_assets" || type == "resource_rewriter" ||
-           type == "java_binary" || type == "group" || type == "java_prebuilt")
+           type == "dist_jar" || type == "android_assets" ||
+           type == "resource_rewriter" || type == "java_binary" ||
+           type == "group" || type == "java_prebuilt")
 
     forward_variables_from(invoker,
                            [
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 1db01006..7abf41c 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1195,6 +1195,86 @@
     }
   }
 
+  # Combines all dependent .jar files into a single .jar file.
+  #
+  # Variables:
+  #   output: Path to the output jar.
+  #   override_build_config: Use a pre-existing .build_config. Must be of type
+  #     "apk".
+  #   use_interface_jars: Use all dependent interface .jars rather than
+  #     implementation .jars.
+  #   direct_deps_only: Do not recurse on deps.
+  #   data, deps, testonly, visibility: Usual meaning.
+  #
+  # Example
+  #   dist_jar("lib_fatjar") {
+  #     deps = [ ":my_java_lib" ]
+  #   }
+  template("dist_jar") {
+    if (defined(invoker.override_build_config)) {
+      _build_config = invoker.override_build_config
+    } else {
+      _build_config = "$target_gen_dir/$target_name.build_config"
+      _build_config_target_name = "${target_name}__build_config"
+
+      write_build_config(_build_config_target_name) {
+        forward_variables_from(invoker, [ "testonly" ])
+        type = "dist_jar"
+        if (defined(invoker.deps)) {
+          possible_config_deps = invoker.deps
+        }
+        build_config = _build_config
+      }
+    }
+
+    action(target_name) {
+      forward_variables_from(invoker,
+                             [
+                               "data",
+                               "deps",
+                               "testonly",
+                               "visibility",
+                             ])
+      script = "//build/android/gyp/create_dist_jar.py"
+      depfile = "$target_gen_dir/$target_name.d"
+
+      inputs = [
+        _build_config,
+      ]
+
+      outputs = [
+        invoker.output,
+      ]
+
+      if (defined(_build_config_target_name)) {
+        deps += [ ":$_build_config_target_name" ]
+      }
+
+      args = [
+        "--depfile",
+        rebase_path(depfile, root_build_dir),
+        "--output",
+        rebase_path(invoker.output, root_build_dir),
+      ]
+
+      _rebased_build_config = rebase_path(_build_config, root_build_dir)
+      if (defined(invoker.direct_deps_only) && invoker.direct_deps_only) {
+        if (defined(invoker.use_interface_jars) && invoker.use_interface_jars) {
+          args += [ "--inputs=@FileArg($_rebased_build_config:javac:interface_classpath)" ]
+        } else {
+          args +=
+              [ "--inputs=@FileArg($_rebased_build_config:javac:classpath)" ]
+        }
+      } else {
+        if (defined(invoker.use_interface_jars) && invoker.use_interface_jars) {
+          args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)" ]
+        } else {
+          args += [ "--inputs=@FileArg($_rebased_build_config:dist_jar:dependency_jars)" ]
+        }
+      }
+    }
+  }
+
   # Declare an Android library target
   #
   # This target creates an Android library containing java code and Android
@@ -1832,28 +1912,16 @@
     # able to just do that calculation at build time instead.
     if (defined(invoker.dist_ijar_path)) {
       _dist_ijar_path = invoker.dist_ijar_path
-      action("${_template_name}_dist_ijar") {
-        script = "//build/android/gyp/create_dist_jar.py"
-        depfile = "$target_gen_dir/$target_name.d"
-        inputs = [
-          _build_config,
-        ]
-        outputs = [
-          "${_dist_ijar_path}",
-        ]
+      dist_jar("${_template_name}_dist_ijar") {
+        override_build_config = _build_config
+        output = _dist_ijar_path
         data = [
           _dist_ijar_path,
         ]
-        args = [
-          "--depfile",
-          rebase_path(depfile, root_build_dir),
-          "--output",
-          rebase_path("${_dist_ijar_path}", root_build_dir),
-          "--inputs=@FileArg($_rebased_build_config:dist_jar:all_interface_jars)",
-        ]
+        use_interface_jars = true
         deps = [
-          ":$build_config_target",  # Generates the build config file.
-          ":$java_target",  # Generates the jar file.
+          ":$build_config_target",
+          ":$java_target",
         ]
       }
     }
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index 8605c86..cc1f28e 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -152,7 +152,7 @@
 }
 
 int RenderSurfaceImpl::TransformTreeIndex() const {
-  return owning_layer_->transform_tree_index();
+  return OwningEffectNode()->transform_id;
 }
 
 int RenderSurfaceImpl::ClipTreeIndex() const {
diff --git a/cc/playback/raster_source.cc b/cc/playback/raster_source.cc
index 6f9365f..031cd30 100644
--- a/cc/playback/raster_source.cc
+++ b/cc/playback/raster_source.cc
@@ -108,13 +108,27 @@
   }
 }
 
+namespace {
+
+bool CanvasIsUnclipped(const SkCanvas* canvas) {
+  if (!canvas->isClipRect())
+    return false;
+
+  SkIRect bounds;
+  if (!canvas->getClipDeviceBounds(&bounds))
+    return false;
+
+  SkISize size = canvas->getBaseLayerSize();
+  return bounds.contains(0, 0, size.width(), size.height());
+}
+
+}  // namespace
+
 void RasterSource::PrepareForPlaybackToCanvas(SkCanvas* canvas) const {
   // TODO(hendrikw): See if we can split this up into separate functions.
 
-  if (canvas->getClipStack()->quickContains(
-          SkRect::MakeFromIRect(canvas->imageInfo().bounds()))) {
+  if (CanvasIsUnclipped(canvas))
     canvas->discard();
-  }
 
   // If this raster source has opaque contents, it is guaranteeing that it will
   // draw an opaque rect the size of the layer.  If it is not, then we must
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 9251bef..4bcac305 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -1278,7 +1278,7 @@
   const EffectNode* effect_node =
       effect_tree.Node(render_surface->EffectTreeIndex());
   // The draw transform of root render surface is identity tranform.
-  if (transform_node->id == 1) {
+  if (transform_node->id == TransformTree::kRootNodeId) {
     render_surface->SetDrawTransform(gfx::Transform());
     return;
   }
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 1bef82ef..2e9ed3e 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -49,6 +49,7 @@
       device_scale_factor_(1.f),
       device_transform_scale_factor_(1.f) {
   cached_data_.push_back(TransformCachedNodeData());
+  cached_data_[kRootNodeId].target_id = kRootNodeId;
 }
 
 TransformTree::~TransformTree() = default;
@@ -117,6 +118,7 @@
   nodes_affected_by_outer_viewport_bounds_delta_.clear();
   cached_data_.clear();
   cached_data_.push_back(TransformCachedNodeData());
+  cached_data_[kRootNodeId].target_id = kRootNodeId;
   sticky_position_data_.clear();
 
 #if DCHECK_IS_ON()
@@ -2067,8 +2069,7 @@
 gfx::Transform PropertyTrees::ToScreenSpaceTransformWithoutSurfaceContentsScale(
     int transform_id,
     int effect_id) const {
-  DCHECK_GT(transform_id, 0);
-  if (transform_id == 1) {
+  if (transform_id == TransformTree::kRootNodeId) {
     return gfx::Transform();
   }
   gfx::Transform screen_space_transform = transform_tree.ToScreen(transform_id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 1d5a8082..b683aa3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -368,9 +368,15 @@
             }
         }
 
+        public void trackSnippetOpened(
+                int windowOpenDisposition, SnippetArticle article, int categoryIndex) {
+            mSnippetsBridge.onSuggestionOpened(article, categoryIndex, windowOpenDisposition);
+        }
+
         @Override
-        public void openSnippet(int windowOpenDisposition, SnippetArticle article) {
-            mSnippetsBridge.onSuggestionOpened(article, windowOpenDisposition);
+        public void openSnippet(
+                int windowOpenDisposition, SnippetArticle article, int categoryIndex) {
+            trackSnippetOpened(windowOpenDisposition, article, categoryIndex);
             NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET);
 
             if (article.mIsAssetDownload) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 78ff652..1915b3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -200,8 +200,9 @@
          * Opens a content suggestion and records related metrics.
          * @param windowOpenDisposition How to open (current tab, new tab, new window etc).
          * @param article The content suggestion to open.
+         * @param categoryIndex The index of the category |article| belongs to.
          */
-        void openSnippet(int windowOpenDisposition, SnippetArticle article);
+        void openSnippet(int windowOpenDisposition, SnippetArticle article, int categoryIndex);
 
         /**
          * Animates the search box up into the omnibox and bring up the keyboard.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
index c4ecfd88..f75a9f3f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -51,7 +51,7 @@
         SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource();
         int[] categories = suggestionsSource.getCategories();
         int[] suggestionsPerCategory = new int[categories.length];
-        int i = 0;
+        int categoryIndex = 0;
         for (int category : categories) {
             int categoryStatus = suggestionsSource.getCategoryStatus(category);
             if (categoryStatus == CategoryStatus.LOADING_ERROR
@@ -59,8 +59,11 @@
                     || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISABLED)
                 continue;
 
-            suggestionsPerCategory[i++] =
+            suggestionsPerCategory[categoryIndex] =
                     resetSection(category, categoryStatus, alwaysAllowEmptySections);
+            SuggestionsSection section = mSections.get(category);
+            if (section != null) section.setCategoryIndex(categoryIndex);
+            ++categoryIndex;
         }
 
         mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPerCategory);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
index 088ee0ac..acb529e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -80,6 +80,7 @@
         private final List<SnippetArticle> mSuggestions = new ArrayList<>();
         private final NewTabPageManager mNewTabPageManager;
         private final SuggestionsCategoryInfo mCategoryInfo;
+        private int mCategoryIndex;
 
         public SuggestionsList(NewTabPageManager newTabPageManager,
                 SuggestionsCategoryInfo categoryInfo) {
@@ -87,6 +88,14 @@
             mCategoryInfo = categoryInfo;
         }
 
+        public void setCategoryIndex(int categoryIndex) {
+            mCategoryIndex = categoryIndex;
+        }
+
+        public int getCategoryIndex() {
+            return mCategoryIndex;
+        }
+
         @Override
         public int getItemCount() {
             return mSuggestions.size();
@@ -105,7 +114,8 @@
             checkIndex(position);
             assert holder instanceof SnippetArticleViewHolder;
             ((SnippetArticleViewHolder) holder)
-                    .onBindViewHolder(getSuggestionAt(position), mCategoryInfo, payloads);
+                    .onBindViewHolder(
+                            getSuggestionAt(position), mCategoryInfo, payloads, mCategoryIndex);
         }
 
         @Override
@@ -426,6 +436,14 @@
         return mCategoryInfo;
     }
 
+    public int getCategoryIndex() {
+        return mSuggestionsList.getCategoryIndex();
+    }
+
+    public void setCategoryIndex(int categoryIndex) {
+        mSuggestionsList.setCategoryIndex(categoryIndex);
+    }
+
     public String getHeaderText() {
         return mHeader.getHeaderText();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
index 913e9c2..7bb3616 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleViewHolder.java
@@ -70,6 +70,7 @@
     private FetchImageCallback mImageCallback;
     private SnippetArticle mArticle;
     private SuggestionsCategoryInfo mCategoryInfo;
+    private int mCategoryIndex;
     private int mPublisherFaviconSizePx;
 
     private final boolean mUseFaviconService;
@@ -117,12 +118,12 @@
 
     @Override
     public void onCardTapped() {
-        mNewTabPageManager.openSnippet(WindowOpenDisposition.CURRENT_TAB, mArticle);
+        mNewTabPageManager.openSnippet(WindowOpenDisposition.CURRENT_TAB, mArticle, mCategoryIndex);
     }
 
     @Override
     public void openItem(int windowDisposition) {
-        mNewTabPageManager.openSnippet(windowDisposition, mArticle);
+        mNewTabPageManager.openSnippet(windowDisposition, mArticle, mCategoryIndex);
     }
 
     @Override
@@ -223,8 +224,8 @@
                 BidiFormatter.getInstance().unicodeWrap(article.mPublisher), relativeTimeSpan);
     }
 
-    public void onBindViewHolder(
-            SnippetArticle article, SuggestionsCategoryInfo categoryInfo, List<Object> payloads) {
+    public void onBindViewHolder(SnippetArticle article, SuggestionsCategoryInfo categoryInfo,
+            List<Object> payloads, int categoryIndex) {
         if (!payloads.isEmpty() && article.equals(mArticle)) {
             performPartialBind(payloads);
             return;
@@ -234,6 +235,7 @@
 
         mArticle = article;
         mCategoryInfo = categoryInfo;
+        mCategoryIndex = categoryIndex;
         updateLayout();
 
         mHeadlineTextView.setText(mArticle.mTitle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
index 4525f1f..a9928ce1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -147,10 +147,11 @@
                 suggestion.mPublishTimestampMilliseconds, suggestion.mScore);
     }
 
-    public void onSuggestionOpened(SnippetArticle suggestion, int windowOpenDisposition) {
+    public void onSuggestionOpened(
+            SnippetArticle suggestion, int categoryIndex, int windowOpenDisposition) {
         assert mNativeSnippetsBridge != 0;
         nativeOnSuggestionOpened(mNativeSnippetsBridge, suggestion.mGlobalPosition,
-                suggestion.mCategory, suggestion.mPosition,
+                suggestion.mCategory, categoryIndex, suggestion.mPosition,
                 suggestion.mPublishTimestampMilliseconds, suggestion.mScore, windowOpenDisposition);
     }
 
@@ -313,8 +314,8 @@
     private native void nativeOnSuggestionShown(long nativeNTPSnippetsBridge, int globalPosition,
             int category, int categoryPosition, long publishTimestampMs, float score);
     private native void nativeOnSuggestionOpened(long nativeNTPSnippetsBridge, int globalPosition,
-            int category, int categoryPosition, long publishTimestampMs, float score,
-            int windowOpenDisposition);
+            int category, int categoryIndex, int categoryPosition, long publishTimestampMs,
+            float score, int windowOpenDisposition);
     private native void nativeOnSuggestionMenuOpened(long nativeNTPSnippetsBridge,
             int globalPosition, int category, int categoryPosition, long publishTimestampMs,
             float score);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java
index 3f356975..8ca0ed4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ContentSuggestionsActivity.java
@@ -109,7 +109,8 @@
         public void trackSnippetCategoryActionClick(int category, int position) {}
 
         @Override
-        public void openSnippet(int windowOpenDisposition, SnippetArticle article) {}
+        public void openSnippet(
+                int windowOpenDisposition, SnippetArticle article, int categoryIndex) {}
 
         @Override
         public void focusSearchBox(boolean beginVoiceSearch, String pastedText) {}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
index 4c37b57..ab722ec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -280,7 +280,8 @@
         public void trackSnippetCategoryActionClick(int category, int position) {}
 
         @Override
-        public void openSnippet(int windowOpenDisposition, SnippetArticle article) {
+        public void openSnippet(
+                int windowOpenDisposition, SnippetArticle article, int categoryIndex) {
             throw new UnsupportedOperationException();
         }
 
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 5bc655f5..7496996f 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6248,6 +6248,24 @@
   <message name="IDS_AD_LOGIN_PASSWORD" desc="Label for Password input field on both AD domain join and AD Authorization user screens..">
     Password
   </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_OLD_PASSWORD" desc="Old password field hint on the Active Directory password change dialog.">
+    Enter old password
+  </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_NEW_PASSWORD" desc="New password field hint on the Active Directory password change dialog.">
+    Enter new password
+  </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_REPEAT_NEW_PASSWORD" desc="Repeat of the new password field hint on the Active Directory password change dialog.">
+    Confirm new password
+  </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_MESSAGE" desc="Message to the user that the Active Directory administrator requires the user to change his/her password.">
+    <ph name="USER_NAME">$1</ph>, your administrator requires you to change your password.
+  </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_INVALID_PASSWORD" desc="Error message for the old password field on the Active Directory password change dialog.">
+    The password you entered is incorrect.
+  </message>
+  <message name="IDS_AD_PASSWORD_CHANGE_PASSWORDS_MISMATCH" desc="Error message in case new password fields does not match on the Active Directory password change dialog.">
+    Passwords do not match.
+  </message>
   <message name="IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_EXPLANATION" desc="Device attribute message to be shown on attribute update prompt screen.">
     Optional — enter new or update existing information to be associated with this device.
   </message>
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.cc b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
index 97be3815..d920bfd 100644
--- a/chrome/browser/android/ntp/ntp_snippets_bridge.cc
+++ b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
@@ -347,6 +347,7 @@
                                            const JavaParamRef<jobject>& obj,
                                            jint global_position,
                                            jint j_category_id,
+                                           jint category_index,
                                            jint category_position,
                                            jlong publish_timestamp_ms,
                                            jfloat score,
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.h b/chrome/browser/android/ntp/ntp_snippets_bridge.h
index 24856e8..2e3f051 100644
--- a/chrome/browser/android/ntp/ntp_snippets_bridge.h
+++ b/chrome/browser/android/ntp/ntp_snippets_bridge.h
@@ -105,6 +105,7 @@
                           const base::android::JavaParamRef<jobject>& obj,
                           jint global_position,
                           jint j_category_id,
+                          jint category_index,
                           jint category_position,
                           jlong publish_timestamp_ms,
                           jfloat score,
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h
index 79d79a2..36c2c56 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.h
+++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -43,6 +43,9 @@
 //       }
 //
 ////////////////////////////////////////////////////////////////////////////////
+//
+// TODO(crbug.com/668114): BrowsingDataRemover does not currently support plugin
+// data deletion. Use PluginDataRemover instead.
 class BrowsingDataRemover {
  public:
   // Mask used for Remove.
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl.cc b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
index 58c7550..710a072 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl.cc
@@ -29,7 +29,6 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/plugin_data_remover.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
 #include "net/base/net_errors.h"
@@ -45,10 +44,6 @@
 #include "storage/browser/quota/special_storage_policy.h"
 #include "url/origin.h"
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
-#endif
-
 using base::UserMetricsAction;
 using content::BrowserContext;
 using content::BrowserThread;
@@ -160,9 +155,6 @@
       remove_mask_(-1),
       origin_type_mask_(-1),
       is_removing_(false),
-#if BUILDFLAG(ENABLE_PLUGINS)
-      flash_lso_helper_(BrowsingDataFlashLSOHelper::Create(browser_context_)),
-#endif
       sub_task_forward_callback_(
           base::Bind(&BrowsingDataRemoverImpl::NotifyIfDone,
                      base::Unretained(this))),
@@ -501,38 +493,6 @@
   }
 
   //////////////////////////////////////////////////////////////////////////////
-  // REMOVE_PLUGINS
-#if BUILDFLAG(ENABLE_PLUGINS)
-  // Plugin is data not separated for protected and unprotected web origins. We
-  // check the origin_type_mask_ to prevent unintended deletion.
-  if (remove_mask & REMOVE_PLUGIN_DATA &&
-      origin_type_mask_ & BrowsingDataHelper::UNPROTECTED_WEB) {
-    content::RecordAction(UserMetricsAction("ClearBrowsingData_LSOData"));
-    clear_plugin_data_count_ = 1;
-
-    if (filter_builder.IsEmptyBlacklist()) {
-      DCHECK(!plugin_data_remover_);
-      plugin_data_remover_.reset(
-          content::PluginDataRemover::Create(browser_context_));
-      base::WaitableEvent* event =
-          plugin_data_remover_->StartRemoving(delete_begin_);
-
-      base::WaitableEventWatcher::EventCallback watcher_callback =
-          base::Bind(&BrowsingDataRemoverImpl::OnWaitableEventSignaled,
-                     weak_ptr_factory_.GetWeakPtr());
-      watcher_.StartWatching(event, watcher_callback);
-    } else {
-      // TODO(msramek): Store filters from the currently executed task on the
-      // object to avoid having to copy them to callback methods.
-      flash_lso_helper_->StartFetching(base::Bind(
-          &BrowsingDataRemoverImpl::OnSitesWithFlashDataFetched,
-          weak_ptr_factory_.GetWeakPtr(),
-          filter_builder.BuildPluginFilter()));
-    }
-  }
-#endif
-
-  //////////////////////////////////////////////////////////////////////////////
   // CACHE
   if (remove_mask & REMOVE_CACHE) {
     // Tell the renderers to clear their cache.
@@ -605,13 +565,6 @@
   storage_partition_for_testing_ = storage_partition;
 }
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-void BrowsingDataRemoverImpl::OverrideFlashLSOHelperForTesting(
-    scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper) {
-  flash_lso_helper_ = flash_lso_helper;
-}
-#endif
-
 const base::Time& BrowsingDataRemoverImpl::GetLastUsedBeginTime() {
   return delete_begin_;
 }
@@ -650,8 +603,7 @@
          !clear_cache_.is_pending() &&
          !clear_channel_ids_.is_pending() &&
          !clear_http_auth_cache_.is_pending() &&
-         !clear_storage_partition_data_.is_pending() &&
-         !clear_plugin_data_count_;
+         !clear_storage_partition_data_.is_pending();
 }
 
 void BrowsingDataRemoverImpl::Notify() {
@@ -708,46 +660,3 @@
 
   Notify();
 }
-
-#if BUILDFLAG(ENABLE_PLUGINS)
-void BrowsingDataRemoverImpl::OnWaitableEventSignaled(
-    base::WaitableEvent* waitable_event) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  DCHECK_EQ(1, clear_plugin_data_count_);
-  clear_plugin_data_count_ = 0;
-
-  plugin_data_remover_.reset();
-  watcher_.StopWatching();
-  NotifyIfDone();
-}
-
-void BrowsingDataRemoverImpl::OnSitesWithFlashDataFetched(
-    base::Callback<bool(const std::string&)> plugin_filter,
-    const std::vector<std::string>& sites) {
-  DCHECK_EQ(1, clear_plugin_data_count_);
-  clear_plugin_data_count_ = 0;
-
-  std::vector<std::string> sites_to_delete;
-  for (const std::string& site : sites) {
-    if (plugin_filter.Run(site))
-      sites_to_delete.push_back(site);
-  }
-
-  clear_plugin_data_count_ = sites_to_delete.size();
-
-  for (const std::string& site : sites_to_delete) {
-    flash_lso_helper_->DeleteFlashLSOsForSite(
-        site,
-        base::Bind(&BrowsingDataRemoverImpl::OnFlashDataDeleted,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  NotifyIfDone();
-}
-
-void BrowsingDataRemoverImpl::OnFlashDataDeleted() {
-  clear_plugin_data_count_--;
-  NotifyIfDone();
-}
-#endif
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl.h b/chrome/browser/browsing_data/browsing_data_remover_impl.h
index 36204eb..1e57134 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl.h
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl.h
@@ -25,12 +25,10 @@
 #include "url/gurl.h"
 
 class BrowsingDataFilterBuilder;
-class BrowsingDataFlashLSOHelper;
 class BrowsingDataRemoverFactory;
 
 namespace content {
 class BrowserContext;
-class PluginDataRemover;
 class StoragePartition;
 }
 
@@ -132,11 +130,6 @@
   void OverrideStoragePartitionForTesting(
       content::StoragePartition* storage_partition);
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-  void OverrideFlashLSOHelperForTesting(
-      scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper);
-#endif
-
  protected:
   // Use BrowsingDataRemoverFactory::GetForBrowserContext to get an instance of
   // this class. The constructor is protected so that the class is mockable.
@@ -191,19 +184,6 @@
   // not already removing, and vice-versa.
   void SetRemoving(bool is_removing);
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-  // Called when plugin data has been cleared. Invokes NotifyIfDone.
-  void OnWaitableEventSignaled(base::WaitableEvent* waitable_event);
-
-  // Called when the list of |sites| storing Flash LSO cookies is fetched.
-  void OnSitesWithFlashDataFetched(
-      base::Callback<bool(const std::string&)> plugin_filter,
-      const std::vector<std::string>& sites);
-
-  // Indicates that LSO cookies for one website have been deleted.
-  void OnFlashDataDeleted();
-#endif
-
   // Executes the next removal task. Called after the previous task was finished
   // or directly from Remove() if the task queue was empty.
   void RunNextTask();
@@ -260,15 +240,6 @@
   // to artificially delay completion. Used for testing.
   static CompletionInhibitor* completion_inhibitor_;
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-  // Used to delete plugin data.
-  std::unique_ptr<content::PluginDataRemover> plugin_data_remover_;
-  base::WaitableEventWatcher watcher_;
-
-  // Used for per-site plugin data deletion.
-  scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper_;
-#endif
-
   // A callback to NotifyIfDone() used by SubTasks instances.
   const base::Closure sub_task_forward_callback_;
 
@@ -280,9 +251,6 @@
   SubTask clear_channel_ids_;
   SubTask clear_http_auth_cache_;
   SubTask clear_storage_partition_data_;
-  // Counts the number of plugin data tasks. Should be the number of LSO cookies
-  // to be deleted, or 1 while we're fetching LSO cookies or deleting in bulk.
-  int clear_plugin_data_count_ = 0;
 
   // Observers of the global state and individual tasks.
   base::ObserverList<Observer, true> observer_list_;
diff --git a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
index 69d02d2..446d9b8d 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
@@ -1111,9 +1111,9 @@
  public:
   explicit RemovePluginDataTester(TestingProfile* profile)
       : helper_(new TestBrowsingDataFlashLSOHelper(profile)) {
-    static_cast<BrowsingDataRemoverImpl*>(
-        BrowsingDataRemoverFactory::GetForBrowserContext(profile))
-            ->OverrideFlashLSOHelperForTesting(helper_);
+    static_cast<ChromeBrowsingDataRemoverDelegate*>(
+        BrowsingDataRemoverFactory::GetForBrowserContext(profile)
+            ->GetEmbedderDelegate())->OverrideFlashLSOHelperForTesting(helper_);
   }
 
   void AddDomain(const std::string& domain) {
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index ac5a6495..c2e79d0 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -64,6 +64,7 @@
 #include "components/previews/core/previews_ui_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/sessions/core/tab_restore_service.h"
+#include "content/public/browser/plugin_data_remover.h"
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/user_metrics.h"
@@ -105,6 +106,10 @@
 #include "chrome/browser/media/webrtc/webrtc_log_util.h"
 #endif
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
+#endif
+
 using base::UserMetricsAction;
 using content::BrowserContext;
 using content::BrowserThread;
@@ -287,6 +292,9 @@
       clear_webrtc_logs_(sub_task_forward_callback_),
 #endif
       clear_auto_sign_in_(sub_task_forward_callback_),
+#if BUILDFLAG(ENABLE_PLUGINS)
+      flash_lso_helper_(BrowsingDataFlashLSOHelper::Create(browser_context)),
+#endif
 #if defined(OS_ANDROID)
       webapp_registry_(new WebappRegistry()),
 #endif
@@ -791,6 +799,46 @@
   }
 
   //////////////////////////////////////////////////////////////////////////////
+  // REMOVE_PLUGINS
+  // Plugins are known to //content and their bulk deletion is implemented in
+  // PluginDataRemover. However, the filtered deletion uses
+  // BrowsingDataFlashLSOHelper which (currently) has strong dependencies
+  // on //chrome.
+  // TODO(msramek): Investigate these dependencies and move the plugin deletion
+  // to BrowsingDataRemoverImpl in //content. Note that code in //content
+  // can simply take advantage of PluginDataRemover directly to delete plugin
+  // data in bulk.
+#if BUILDFLAG(ENABLE_PLUGINS)
+  // Plugin is data not separated for protected and unprotected web origins. We
+  // check the origin_type_mask_ to prevent unintended deletion.
+  if (remove_mask & BrowsingDataRemover::REMOVE_PLUGIN_DATA &&
+      origin_type_mask & BrowsingDataHelper::UNPROTECTED_WEB) {
+    content::RecordAction(UserMetricsAction("ClearBrowsingData_LSOData"));
+    clear_plugin_data_count_ = 1;
+
+    if (filter_builder.IsEmptyBlacklist()) {
+      DCHECK(!plugin_data_remover_);
+      plugin_data_remover_.reset(
+          content::PluginDataRemover::Create(profile_));
+      base::WaitableEvent* event =
+          plugin_data_remover_->StartRemoving(delete_begin_);
+
+      base::WaitableEventWatcher::EventCallback watcher_callback = base::Bind(
+          &ChromeBrowsingDataRemoverDelegate::OnWaitableEventSignaled,
+          weak_ptr_factory_.GetWeakPtr());
+      watcher_.StartWatching(event, watcher_callback);
+    } else {
+      // TODO(msramek): Store filters from the currently executed task on the
+      // object to avoid having to copy them to callback methods.
+      flash_lso_helper_->StartFetching(base::Bind(
+          &ChromeBrowsingDataRemoverDelegate::OnSitesWithFlashDataFetched,
+          weak_ptr_factory_.GetWeakPtr(),
+          filter_builder.BuildPluginFilter()));
+    }
+  }
+#endif
+
+  //////////////////////////////////////////////////////////////////////////////
   // REMOVE_MEDIA_LICENSES
   if (remove_mask & BrowsingDataRemover::REMOVE_MEDIA_LICENSES) {
     // TODO(jrummell): This UMA should be renamed to indicate it is for Media
@@ -903,7 +951,8 @@
 #if BUILDFLAG(ENABLE_WEBRTC)
          !clear_webrtc_logs_.is_pending() &&
 #endif
-         !clear_auto_sign_in_.is_pending();
+         !clear_auto_sign_in_.is_pending() &&
+         !clear_plugin_data_count_;
 }
 
 #if defined(OS_ANDROID)
@@ -913,6 +962,13 @@
 }
 #endif
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+void ChromeBrowsingDataRemoverDelegate::OverrideFlashLSOHelperForTesting(
+    scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper) {
+  flash_lso_helper_ = flash_lso_helper;
+}
+#endif
+
 void ChromeBrowsingDataRemoverDelegate::OnKeywordsLoaded(
     base::Callback<bool(const GURL&)> url_filter) {
   // Deletes the entries from the model.
@@ -932,16 +988,6 @@
   NotifyIfDone();
 }
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-void ChromeBrowsingDataRemoverDelegate::
-OnDeauthorizeFlashContentLicensesCompleted(
-    uint32_t request_id,
-    bool /* success */) {
-  DCHECK_EQ(request_id, deauthorize_flash_content_licenses_request_id_);
-  clear_flash_content_licenses_.GetCompletionCallback().Run();
-}
-#endif
-
 #if defined(OS_CHROMEOS)
 void ChromeBrowsingDataRemoverDelegate::OnClearPlatformKeys(
     chromeos::DBusMethodCallStatus call_status,
@@ -951,3 +997,54 @@
   clear_platform_keys_.GetCompletionCallback().Run();
 }
 #endif
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+void ChromeBrowsingDataRemoverDelegate::OnWaitableEventSignaled(
+    base::WaitableEvent* waitable_event) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  DCHECK_EQ(1, clear_plugin_data_count_);
+  clear_plugin_data_count_ = 0;
+
+  plugin_data_remover_.reset();
+  watcher_.StopWatching();
+  NotifyIfDone();
+}
+
+void ChromeBrowsingDataRemoverDelegate::OnSitesWithFlashDataFetched(
+    base::Callback<bool(const std::string&)> plugin_filter,
+    const std::vector<std::string>& sites) {
+  DCHECK_EQ(1, clear_plugin_data_count_);
+  clear_plugin_data_count_ = 0;
+
+  std::vector<std::string> sites_to_delete;
+  for (const std::string& site : sites) {
+    if (plugin_filter.Run(site))
+      sites_to_delete.push_back(site);
+  }
+
+  clear_plugin_data_count_ = sites_to_delete.size();
+
+  for (const std::string& site : sites_to_delete) {
+    flash_lso_helper_->DeleteFlashLSOsForSite(
+        site,
+        base::Bind(&ChromeBrowsingDataRemoverDelegate::OnFlashDataDeleted,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  NotifyIfDone();
+}
+
+void ChromeBrowsingDataRemoverDelegate::OnFlashDataDeleted() {
+  clear_plugin_data_count_--;
+  NotifyIfDone();
+}
+
+void ChromeBrowsingDataRemoverDelegate::
+OnDeauthorizeFlashContentLicensesCompleted(
+    uint32_t request_id,
+    bool /* success */) {
+  DCHECK_EQ(request_id, deauthorize_flash_content_licenses_request_id_);
+  clear_flash_content_licenses_.GetCompletionCallback().Run();
+}
+#endif
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
index 3814224..ac542631 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h
@@ -29,11 +29,13 @@
 #include "chromeos/dbus/dbus_method_call_status.h"
 #endif
 
+class BrowsingDataFlashLSOHelper;
 class Profile;
 class WebappRegistry;
 
 namespace content {
 class BrowserContext;
+class PluginDataRemover;
 }
 
 // A delegate used by BrowsingDataRemover to delete data specific to Chrome
@@ -87,6 +89,12 @@
       std::unique_ptr<WebappRegistry> webapp_registry);
 #endif
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+  // Used for testing.
+  void OverrideFlashLSOHelperForTesting(
+      scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper);
+#endif
+
  private:
   // If AllDone(), calls the callback provided in RemoveEmbedderData().
   void NotifyIfDone();
@@ -107,6 +115,17 @@
   void OnClearedCookies();
 
 #if BUILDFLAG(ENABLE_PLUGINS)
+  // Called when plugin data has been cleared. Invokes NotifyIfDone.
+  void OnWaitableEventSignaled(base::WaitableEvent* waitable_event);
+
+  // Called when the list of |sites| storing Flash LSO cookies is fetched.
+  void OnSitesWithFlashDataFetched(
+      base::Callback<bool(const std::string&)> plugin_filter,
+      const std::vector<std::string>& sites);
+
+  // Indicates that LSO cookies for one website have been deleted.
+  void OnFlashDataDeleted();
+
   // PepperFlashSettingsManager::Client implementation.
   void OnDeauthorizeFlashContentLicensesCompleted(uint32_t request_id,
                                                   bool success) override;
@@ -152,13 +171,22 @@
   SubTask clear_precache_history_;
   SubTask clear_offline_page_data_;
 #endif
-
 #if BUILDFLAG(ENABLE_WEBRTC)
   SubTask clear_webrtc_logs_;
 #endif
   SubTask clear_auto_sign_in_;
+  // Counts the number of plugin data tasks. Should be the number of LSO cookies
+  // to be deleted, or 1 while we're fetching LSO cookies or deleting in bulk.
+  int clear_plugin_data_count_ = 0;
 
 #if BUILDFLAG(ENABLE_PLUGINS)
+  // Used to delete plugin data.
+  std::unique_ptr<content::PluginDataRemover> plugin_data_remover_;
+  base::WaitableEventWatcher watcher_;
+
+  // Used for per-site plugin data deletion.
+  scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper_;
+
   uint32_t deauthorize_flash_content_licenses_request_id_ = 0;
 
   // Used to deauthorize content licenses for Pepper Flash.
diff --git a/chrome/browser/chromeos/login/oobe_screen.cc b/chrome/browser/chromeos/login/oobe_screen.cc
index 0f0cdf8..92678791 100644
--- a/chrome/browser/chromeos/login/oobe_screen.cc
+++ b/chrome/browser/chromeos/login/oobe_screen.cc
@@ -41,6 +41,8 @@
     "device-disabled",                 // SCREEN_DEVICE_DISABLED
     "unrecoverable-cryptohome-error",  // SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR
     "userBoard",                       // SCREEN_USER_SELECTION
+    // SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE
+    "ad-password-change",
     "login",                           // SCREEN_SPECIAL_LOGIN
     "oobe",                            // SCREEN_SPECIAL_OOBE
     "test:nowindow",                   // SCREEN_TEST_NO_WINDOW
diff --git a/chrome/browser/chromeos/login/oobe_screen.h b/chrome/browser/chromeos/login/oobe_screen.h
index 1e31069a..7f2a334 100644
--- a/chrome/browser/chromeos/login/oobe_screen.h
+++ b/chrome/browser/chromeos/login/oobe_screen.h
@@ -43,6 +43,7 @@
   SCREEN_DEVICE_DISABLED,
   SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR,
   SCREEN_USER_SELECTION,
+  SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE,
 
   // Special "first screen" that initiates login flow.
   SCREEN_SPECIAL_LOGIN,
diff --git a/chrome/browser/chromeos/login/screens/core_oobe_actor.h b/chrome/browser/chromeos/login/screens/core_oobe_actor.h
index 92f5a0f..1200caa 100644
--- a/chrome/browser/chromeos/login/screens/core_oobe_actor.h
+++ b/chrome/browser/chromeos/login/screens/core_oobe_actor.h
@@ -43,6 +43,8 @@
   virtual void InitDemoModeDetection() = 0;
   virtual void StopDemoModeDetection() = 0;
   virtual void UpdateKeyboardState() = 0;
+  virtual void ShowActiveDirectoryPasswordChangeScreen(
+      const std::string& username) = 0;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/resources/chromeos/login/active_directory_password_change.html b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
new file mode 100644
index 0000000..3f21f19
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
@@ -0,0 +1,67 @@
+<!-- Copyright 2016 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. -->
+
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-in-animation.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-out-animation.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animated-pages.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
+
+<!--
+  Offline UI for the Active Directory password change.
+
+  Attributes:
+    'username' - User principal name.
+  Methods:
+    'reset' - resets to the initial state.
+  Events:
+    'authCompleted' - Fired when user enters old password and confirms new one.
+                      Fires with an argument which contains:
+                      { 'username': <username>,
+                        'oldPassword': <typed old password>,
+                        'newPassword': <typed new password>,
+                      }
+-->
+<dom-module id="active-directory-password-change">
+  <link rel="stylesheet" href="gaia_password_changed.css">
+  <template>
+    <neon-animated-pages id="animatedPages" class="fit"
+        entry-animation="fade-in-animation" exit-animation="fade-out-animation"
+        selected="0">
+      <neon-animatable class="fit">
+        <gaia-card id="gaiaCard" class="fit">
+          <div class="header flex vertical layout end-justified start">
+            <h1 id="welcomeMessage" class="welcome-message">
+              [[computeWelcomeMessage_(username)]]
+            </h1>
+          </div>
+          <div class="footer flex vertical layout justified">
+            <gaia-input-form on-submit="onSubmit_"
+                i18n-values="button-text:offlineLoginNextBtn">
+              <gaia-input id="oldPassword" type="password" required
+                  i18n-values="error:adOldPasswordError;
+                               label:adEnterOldPasswordHint">
+              </gaia-input>
+              <gaia-input id="newPassword1" type="password" required
+                  i18n-values="label:adEnterNewPasswordHint">
+              </gaia-input>
+              <gaia-input id="newPassword2" type="password" required
+                  i18n-values="error:adNewPasswordError;
+                               label:adRepeatNewPasswordHint">
+              </gaia-input>
+            </gaia-input-form>
+          </div>
+        </gaia-card>
+      </neon-animatable>
+      <neon-animatable class="fit">
+        <throbber-notice class="fit" i18n-values="text:gaiaLoading">
+        </throbber-notice>
+      </neon-animatable>
+    </neon-animated-pages>
+    <navigation-bar id="navigation" close-visible on-close="onClose_">
+    </navigation-bar>
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/active_directory_password_change.js b/chrome/browser/resources/chromeos/login/active_directory_password_change.js
new file mode 100644
index 0000000..2ba38f7e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/active_directory_password_change.js
@@ -0,0 +1,66 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview Polymer element for Active Directory password change screen.
+ */
+Polymer({
+  is: 'active-directory-password-change',
+
+  properties: {
+    /**
+     * The user principal name.
+     */
+    username: String,
+  },
+
+  /** @public */
+  reset: function() {
+    this.$.animatedPages.selected = 0;
+    this.$.oldPassword.value = '';
+    this.$.newPassword1.value = '';
+    this.$.newPassword2.value = '';
+    this.updateNavigation_();
+  },
+
+  /** @private */
+  computeWelcomeMessage_: function(username) {
+    return loadTimeData.getStringF('adPasswordChangeMessage', username);
+  },
+
+  /** @private */
+  onSubmit_: function() {
+    if (!this.$.oldPassword.checkValidity() ||
+        !this.$.newPassword1.checkValidity()) {
+      return;
+    }
+    if (this.$.newPassword1.value != this.$.newPassword2.value) {
+      this.$.newPassword2.isInvalid = true;
+      return;
+    }
+    this.$.animatedPages.selected++;
+    this.updateNavigation_();
+    var msg = {
+      'username': this.username,
+      'oldPassword': this.$.oldPassword.value,
+      'newPassword': this.$.newPassword1.value,
+    };
+    this.$.oldPassword.value = '';
+    this.$.newPassword1.value = '';
+    this.$.newPassword2.value = '';
+    this.fire('authCompleted', msg);
+  },
+
+  /** @private */
+  onClose_: function() {
+    if (!this.$.navigation.closeVisible)
+      return;
+    this.fire('cancel');
+  },
+
+  /** @private */
+  updateNavigation_: function() {
+    this.$.navigation.closeVisible = (this.$.animatedPages.selected == 0);
+  },
+});
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html
index 02c4cb76..a4551a9 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -12,5 +12,6 @@
 <include src="navigation_bar.html">
 <include src="unrecoverable_cryptohome_error_card.html">
 <include src="offline_ad_login.html">
+<include src="active_directory_password_change.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js
index 691b243f..3b04fc93 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -16,3 +16,4 @@
 // <include src="navigation_bar.js">
 // <include src="unrecoverable_cryptohome_error_card.js">
 // <include src="offline_ad_login.js">
+// <include src="active_directory_password_change.js">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index 05f00cf..112bb89 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -24,5 +24,6 @@
 <include src="oobe_welcome_dialog.html">
 <include src="oobe_welcome.html">
 <include src="offline_ad_login.html">
+<include src="active_directory_password_change.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index 663e622..ab6875f 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -34,3 +34,4 @@
 // <include src="oobe_welcome_dialog.js">
 // <include src="oobe_welcome.js">
 // <include src="offline_ad_login.js">
+// <include src="active_directory_password_change.js">
diff --git a/chrome/browser/resources/chromeos/login/login.js b/chrome/browser/resources/chromeos/login/login.js
index 002345b..8580484 100644
--- a/chrome/browser/resources/chromeos/login/login.js
+++ b/chrome/browser/resources/chromeos/login/login.js
@@ -36,6 +36,7 @@
       login.FatalErrorScreen.register();
       login.DeviceDisabledScreen.register();
       login.UnrecoverableCryptohomeErrorScreen.register();
+      login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true);
 
       cr.ui.Bubble.decorate($('bubble'));
       login.HeaderBar.decorate($('login-header-bar'));
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
index 4154892..4d477db 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
@@ -32,6 +32,7 @@
 <link rel="stylesheet" href="screen_fatal_error.css">
 <link rel="stylesheet" href="screen_device_disabled.css">
 <link rel="stylesheet" href="screen_unrecoverable_cryptohome_error.css">
+<link rel="stylesheet" href="screen_active_directory_password_change.css">
 
 <link rel="stylesheet" href="../../options/chromeos/bluetooth.css">
 
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
index a0d72e1..0153ca3 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
@@ -29,6 +29,7 @@
 // <include src="screen_fatal_error.js">
 // <include src="screen_device_disabled.js">
 // <include src="screen_unrecoverable_cryptohome_error.js">
+// <include src="screen_active_directory_password_change.js">
 
 // <include src="../../gaia_auth_host/authenticator.js">
 
diff --git a/chrome/browser/resources/chromeos/login/login_screens.html b/chrome/browser/resources/chromeos/login/login_screens.html
index 8b928823..765877c 100644
--- a/chrome/browser/resources/chromeos/login/login_screens.html
+++ b/chrome/browser/resources/chromeos/login/login_screens.html
@@ -16,3 +16,4 @@
 <include src="screen_fatal_error.html">
 <include src="screen_device_disabled.html">
 <include src="screen_unrecoverable_cryptohome_error.html">
+<include src="screen_active_directory_password_change.html">
diff --git a/chrome/browser/resources/chromeos/login/login_shared.js b/chrome/browser/resources/chromeos/login/login_shared.js
index ff3b386..160da795 100644
--- a/chrome/browser/resources/chromeos/login/login_shared.js
+++ b/chrome/browser/resources/chromeos/login/login_shared.js
@@ -187,6 +187,14 @@
   };
 
   /**
+   * Shows Active Directory password change screen.
+   * @param {string} username Name of the user that should change the password.
+   */
+  Oobe.showActiveDirectoryPasswordChangeScreen = function(username) {
+    DisplayManager.showActiveDirectoryPasswordChangeScreen(username);
+  };
+
+  /**
    * Show user-pods.
    */
   Oobe.showUserPods = function() {
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index 1659cdc2..6386ed6 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -117,6 +117,7 @@
       login.ControllerPairingScreen.register();
       login.HostPairingScreen.register();
       login.DeviceDisabledScreen.register();
+      login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true);
 
       cr.ui.Bubble.decorate($('bubble'));
       login.HeaderBar.decorate($('login-header-bar'));
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index fc8a619f..ad896b41 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -23,3 +23,4 @@
 <include src="screen_confirm_password.html">
 <include src="screen_fatal_error.html">
 <include src="screen_device_disabled.html">
+<include src="screen_active_directory_password_change.html">
diff --git a/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.css b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.css
new file mode 100644
index 0000000..81a55ef
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.css
@@ -0,0 +1,14 @@
+/* Copyright 2016 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.
+ */
+#ad-password-change {
+  bottom: 0;
+  display: block;
+  height: 528px; /* Should be the same as #gaia-signin. */
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 448px; /* Should be the same as #gaia-signin. */
+}
diff --git a/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.html b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.html
new file mode 100644
index 0000000..8dc8f72
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.html
@@ -0,0 +1,8 @@
+<link rel="import" href="chrome://oobe/custom_elements.html">
+
+<div id="ad-password-change" class="step faded hidden migrate no-logo"
+    hidden>
+  <active-directory-password-change id="active-directory-password-change"
+      class="fit">
+  </active-directory-password-change>
+</div>
diff --git a/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js
new file mode 100644
index 0000000..ec04944
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js
@@ -0,0 +1,52 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview Active Directory password change screen implementation.
+ */
+login.createScreen('ActiveDirectoryPasswordChangeScreen', 'ad-password-change',
+    function() {
+  return {
+    EXTERNAL_API: [
+      'show'
+    ],
+
+    adPasswordChanged_: null,
+
+    /** @override */
+    decorate: function() {
+      this.adPasswordChanged_ = $('active-directory-password-change');
+      this.adPasswordChanged_.addEventListener('cancel',
+                                               this.cancel.bind(this));
+
+      this.adPasswordChanged_.addEventListener('authCompleted',
+          function(e) {
+            chrome.send('completeAdPasswordChange',
+                [e.detail.username,
+                 e.detail.oldPassword,
+                 e.detail.newPassword]);
+          });
+    },
+
+    /**
+     * Cancels password changing and drops the user back to the login screen.
+     */
+    cancel: function() {
+      Oobe.showUserPods();
+    },
+
+    /**
+     * Shows password changed screen.
+     * @param {string} username Name of user that should change the password.
+     */
+    show: function(username) {
+      // We'll get here after the successful Active Directory authentication.
+      // It assumes session is about to start so hides login screen controls.
+      Oobe.getInstance().headerHidden = true;
+      Oobe.showScreen({id: SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE});
+      this.adPasswordChanged_.reset();
+      this.adPasswordChanged_.username = username;
+    }
+  };
+});
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index fb9e2108..ab3fa19d 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -612,6 +612,8 @@
 }
 
 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
+  [tabStripController_ setVisualEffectsDisabledForFullscreen:YES];
+
   // In Yosemite, some combination of the titlebar and toolbar always show in
   // full-screen mode. We do not want either to show. Search for the window that
   // contains the views, and hide it. There is no need to ever unhide the view.
@@ -678,6 +680,8 @@
 }
 
 - (void)windowWillExitFullScreen:(NSNotification*)notification {
+  [tabStripController_ setVisualEffectsDisabledForFullscreen:NO];
+
   if (fullscreenLowPowerCoordinator_)
     fullscreenLowPowerCoordinator_->SetInFullscreenTransition(true);
 
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
index a28a7fb..a22fc45 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_unittest.mm
@@ -700,6 +700,25 @@
   EXPECT_TRUE([[contentView hitTest:point] isDescendantOf:bookmarkView]);
 }
 
+// Check that when the window becomes/resigns main, the tab strip's background
+// view is redrawn.
+TEST_F(BrowserWindowControllerTest, TabStripBackgroundViewRedrawTest) {
+  NSView* view = controller_.tabStripBackgroundView;
+  id partial_mock = [OCMockObject partialMockForObject:view];
+
+  [[partial_mock expect] setNeedsDisplay:YES];
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidBecomeMainNotification
+                    object:controller_.window];
+  [partial_mock verify];
+
+  [[partial_mock expect] setNeedsDisplay:YES];
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidResignMainNotification
+                    object:controller_.window];
+  [partial_mock verify];
+}
+
 @interface BrowserWindowControllerFakeFullscreen : BrowserWindowController {
  @private
   // We release the window ourselves, so we don't have to rely on the unittest
diff --git a/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
index 43d28e1..2855e87 100644
--- a/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
+++ b/chrome/browser/ui/cocoa/browser_window_fullscreen_transition.mm
@@ -54,6 +54,40 @@
 
 }  // namespace
 
+// This view draws a dummy toolbar over the resized content view during
+// the exit fullscreen animation. It is removed at the end of the animation.
+@interface FullscreenTabStripBackgroundView : NSView {
+  base::scoped_nsobject<NSColor> windowBackgroundColor_;
+}
+
+- (instancetype)initWithFrame:(NSRect)frame background:(NSColor*)color;
+
+@end
+
+@implementation FullscreenTabStripBackgroundView
+
+- (instancetype)initWithFrame:(NSRect)frame background:(NSColor*)color {
+  if ((self = [super initWithFrame:frame])) {
+    windowBackgroundColor_.reset([color copy]);
+  }
+  return self;
+}
+
+// Override this method so that we can paint the toolbar in this view.
+// This method first fill itself with the toolbar's background. After that,
+// it will paint the window's theme if applicable.
+- (void)drawRect:(NSRect)frame {
+  [windowBackgroundColor_ set];
+  NSRectFillUsingOperation(frame, NSCompositeDestinationOver);
+
+  [FramedBrowserWindow drawWindowThemeInDirtyRect:frame
+                                          forView:self
+                                           bounds:[self bounds]
+                             forceBlackBackground:NO];
+}
+
+@end
+
 @interface BrowserWindowFullscreenTransition ()
     <CAAnimationDelegate, CALayerDelegate> {
   // Flag to keep track of whether we are entering or exiting fullscreen.
@@ -100,7 +134,7 @@
   NSRect finalFrame_;
 
   // This view draws the tabstrip background during the exit animation.
-  base::scoped_nsobject<TabStripBackgroundView>
+  base::scoped_nsobject<FullscreenTabStripBackgroundView>
       fullscreenTabStripBackgroundView_;
 
   // Locks and unlocks the FullSizeContentWindow.
@@ -328,7 +362,9 @@
     [primaryWindow_ forceContentViewFrame:relativeContentFinalFrame];
 
     fullscreenTabStripBackgroundView_.reset(
-        [[TabStripBackgroundView alloc] initWithFrame:finalFrame_]);
+        [[FullscreenTabStripBackgroundView alloc]
+            initWithFrame:finalFrame_
+               background:primaryWindowInitialBackgroundColor_]);
     [fullscreenTabStripBackgroundView_ setFrameOrigin:NSZeroPoint];
     [contentView addSubview:fullscreenTabStripBackgroundView_.get()
                  positioned:NSWindowBelow
diff --git a/chrome/browser/ui/cocoa/floating_bar_backing_view.h b/chrome/browser/ui/cocoa/floating_bar_backing_view.h
index a55b1239..bfd4114 100644
--- a/chrome/browser/ui/cocoa/floating_bar_backing_view.h
+++ b/chrome/browser/ui/cocoa/floating_bar_backing_view.h
@@ -7,10 +7,10 @@
 
 #import <Cocoa/Cocoa.h>
 
-#import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h"
+#import "chrome/browser/ui/cocoa/themed_window.h"
 
 // A custom view that draws the tab strip background for fullscreen windows.
-@interface FloatingBarBackingView : TabStripBackgroundView
+@interface FloatingBarBackingView : NSView<ThemedWindowDrawing>
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_FLOATING_BAR_BACKING_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/floating_bar_backing_view.mm b/chrome/browser/ui/cocoa/floating_bar_backing_view.mm
index b4c34d38..96a5fff 100644
--- a/chrome/browser/ui/cocoa/floating_bar_backing_view.mm
+++ b/chrome/browser/ui/cocoa/floating_bar_backing_view.mm
@@ -4,10 +4,28 @@
 
 #include "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
 
+#import "chrome/browser/ui/cocoa/framed_browser_window.h"
 #import "ui/base/cocoa/appkit_utils.h"
 
 @implementation FloatingBarBackingView
 
+- (void)drawRect:(NSRect)rect {
+  NSWindow* window = [self window];
+  BOOL isMainWindow = [window isMainWindow];
+
+  if (isMainWindow)
+    [[NSColor windowFrameColor] set];
+  else
+    [[NSColor windowBackgroundColor] set];
+  NSRectFill(rect);
+
+  [FramedBrowserWindow drawWindowThemeInDirtyRect:rect
+                                          forView:self
+                                           bounds:[self bounds]
+                             forceBlackBackground:YES];
+
+}
+
 // Eat all mouse events (and do *not* pass them on to the next responder!).
 - (void)mouseDown:(NSEvent*)event {}
 - (void)rightMouseDown:(NSEvent*)event {}
@@ -27,4 +45,14 @@
     ui::WindowTitlebarReceivedDoubleClick([self window], self);
 }
 
+// ThemedWindowDrawing implementation.
+
+- (void)windowDidChangeTheme {
+  [self setNeedsDisplay:YES];
+}
+
+- (void)windowDidChangeActive {
+  [self setNeedsDisplay:YES];
+}
+
 @end  // @implementation FloatingBarBackingView
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h
index f75f7e0..5639540 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h
@@ -9,11 +9,11 @@
 
 #import "chrome/browser/ui/cocoa/themed_window.h"
 
-// A view that draws the theme and transparency effects behind the tab strip.
-// It should be behind its overlapping sibling views (window controls, tab
-// strip view, profile button and fullscreen button).
+// A view that draws the theme image in the top area of the window (behind the
+// tab strip area). It should be arranged so that its z-order is below its
+// overlapping sibling views (window controls, tab strip view, profile button
+// and fullscreen button).
 @interface TabStripBackgroundView : NSView<ThemedWindowDrawing>
-@property(nonatomic) BOOL inATabDraggingOverlayWindow;
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_TABS_TAB_STRIP_BACKGROUND_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm
index 5d6e03f0..4b32010 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view.mm
@@ -4,43 +4,21 @@
 
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h"
 
-#include "chrome/browser/themes/theme_properties.h"
 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
 #import "ui/base/cocoa/nsview_additions.h"
-#include "ui/base/theme_provider.h"
 
-// TODO(sdy): Remove once we no longer support 10.9 (-Wunguarded-availability).
-@class NSVisualEffectView;
-extern NSString* const NSAppearanceNameVibrantDark;
-extern NSString* const NSAppearanceNameVibrantLight;
-
-@interface NSAppearance (AllowsVibrancy)
-@property(readonly) BOOL allowsVibrancy;
-@end
-// /TODO
-
-@interface TabStripThemeBackgroundView : NSView
-@property(nonatomic) BOOL inATabDraggingOverlayWindow;
-@end
-
-@implementation TabStripThemeBackgroundView
-
-@synthesize inATabDraggingOverlayWindow = inATabDraggingOverlayWindow_;
+@implementation TabStripBackgroundView
 
 - (void)drawRect:(NSRect)dirtyRect {
   // Only the top corners are rounded. For simplicity, round all 4 corners but
   // draw the bottom corners outside of the visible bounds.
   float cornerRadius = 4.0;
-  bool isFullscreen = (self.window.styleMask & NSFullScreenWindowMask) != 0;
-
   NSRect roundedRect = [self bounds];
-  if (!isFullscreen) {
-    roundedRect.origin.y -= cornerRadius;
-    roundedRect.size.height += cornerRadius;
-    [[NSBezierPath bezierPathWithRoundedRect:roundedRect
-                                     xRadius:cornerRadius
-                                     yRadius:cornerRadius] addClip];
-  }
+  roundedRect.origin.y -= cornerRadius;
+  roundedRect.size.height += cornerRadius;
+  [[NSBezierPath bezierPathWithRoundedRect:roundedRect
+                                   xRadius:cornerRadius
+                                   yRadius:cornerRadius] addClip];
   BOOL themed = [FramedBrowserWindow drawWindowThemeInDirtyRect:dirtyRect
                                                         forView:self
                                                          bounds:roundedRect
@@ -48,151 +26,28 @@
 
   // Draw a 1px border on the top edge and top corners.
   if (themed) {
-    if (!isFullscreen) {
-      CGFloat lineWidth = [self cr_lineWidth];
-      // Inset the vertical lines by 0.5px so that the top line gets a full
-      // pixel. Outset the horizontal lines by 0.5px so that they are not
-      // visible, but still get the rounded corners to get a border.
-      NSRect strokeRect =
-          NSInsetRect(roundedRect, -lineWidth / 2, lineWidth / 2);
-      NSBezierPath* path =
-          [NSBezierPath bezierPathWithRoundedRect:strokeRect
-                                          xRadius:cornerRadius
-                                          yRadius:cornerRadius];
-      [path setLineWidth:lineWidth];
-      [[NSColor colorWithCalibratedWhite:1.0 alpha:0.5] set];
-      [path stroke];
-    }
-  } else if (!inATabDraggingOverlayWindow_) {
-    // If the window is not themed, and not being used to drag tabs between
-    // browser windows, decrease the tab strip background's translucency by
-    // overlaying it with a partially-transparent gray. The gray is somewhat
-    // opaque for Incognito mode, very opaque for non-Incognito mode, and
-    // completely opaque when the window is not active.
-    if (const ui::ThemeProvider* themeProvider =
-            [[self window] themeProvider]) {
-      NSColor* overlayColor = nil;
-      if (self.window.isMainWindow) {
-        NSAppearance* appearance = self.effectiveAppearance;
-        if ([appearance respondsToSelector:@selector(allowsVibrancy)] &&
-            appearance.allowsVibrancy) {
-          overlayColor = themeProvider->GetNSColor(
-              ThemeProperties::COLOR_FRAME_VIBRANCY_OVERLAY);
-        } else if (themeProvider->InIncognitoMode()) {
-          overlayColor = [NSColor colorWithSRGBRed:20 / 255.
-                                             green:22 / 255.
-                                              blue:24 / 255.
-                                             alpha:1];
-        } else {
-          overlayColor =
-              themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME);
-        }
-      } else {
-        overlayColor =
-            themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME_INACTIVE);
-      }
-
-      if (overlayColor) {
-        [overlayColor set];
-        NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
-      }
-    }
+    CGFloat lineWidth = [self cr_lineWidth];
+    // Inset the vertical lines by 0.5px so that the top line gets a full pixel.
+    // Outset the horizontal lines by 0.5px so that they are not visible, but
+    // still get the rounded corners to get a border.
+    NSRect strokeRect = NSInsetRect(roundedRect, -lineWidth/2, lineWidth/2);
+    NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:strokeRect
+                                                         xRadius:cornerRadius
+                                                         yRadius:cornerRadius];
+    [path setLineWidth:lineWidth];
+    [[NSColor colorWithCalibratedWhite:1.0 alpha:0.5] set];
+    [path stroke];
   }
 }
-@end
-
-@implementation TabStripBackgroundView {
-  // Weak, owned by its superview.
-  TabStripThemeBackgroundView* themeBackgroundView_;
-
-  // Weak, owned by its superview.
-  NSVisualEffectView* visualEffectView_;
-}
-
-- (instancetype)initWithFrame:(NSRect)frame {
-  if ((self = [super initWithFrame:frame])) {
-    themeBackgroundView_ = [[[TabStripThemeBackgroundView alloc]
-        initWithFrame:self.bounds] autorelease];
-    themeBackgroundView_.autoresizingMask =
-        NSViewWidthSizable | NSViewHeightSizable;
-    [self addSubview:themeBackgroundView_];
-  }
-  return self;
-}
-
-- (BOOL)inATabDraggingOverlayWindow {
-  return themeBackgroundView_.inATabDraggingOverlayWindow;
-}
-
-- (void)setInATabDraggingOverlayWindow:(BOOL)inATabDraggingOverlayWindow {
-  themeBackgroundView_.inATabDraggingOverlayWindow =
-      inATabDraggingOverlayWindow;
-}
-
-- (void)updateVisualEffectView {
-  const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
-  const bool isFullscreen =
-      (self.window.styleMask & NSFullScreenWindowMask) != 0;
-
-  // Visual effects cause higher power consumption in full screen.
-  if (!isFullscreen && themeProvider->UsingSystemTheme()) {
-    if (!visualEffectView_) {
-      visualEffectView_ =
-          [[[NSVisualEffectView alloc] initWithFrame:self.bounds] autorelease];
-      if (!visualEffectView_)
-        return;
-
-      visualEffectView_.autoresizingMask =
-          NSViewWidthSizable | NSViewHeightSizable;
-      visualEffectView_.appearance =
-          [NSAppearance appearanceNamed:themeProvider->InIncognitoMode()
-                                            ? NSAppearanceNameVibrantDark
-                                            : NSAppearanceNameVibrantLight];
-      [self addSubview:visualEffectView_];
-      [visualEffectView_ addSubview:themeBackgroundView_];
-    }
-  } else {
-    if (visualEffectView_) {
-      [self addSubview:themeBackgroundView_];
-      [visualEffectView_ removeFromSuperview];
-      visualEffectView_ = nil;
-    }
-  }
-}
-
-- (void)viewWillMoveToWindow:(NSWindow*)newWindow {
-  // AppKit calls this method when the view's position in the view hierarchy
-  // changes, even if its parent window won't change.
-  if (newWindow == self.window)
-    return;
-  if (self.window)
-    [self.window removeObserver:self forKeyPath:@"styleMask"];
-  if (newWindow)
-    [newWindow addObserver:self forKeyPath:@"styleMask" options:0 context:nil];
-}
-
-- (void)observeValueForKeyPath:(NSString*)keyPath
-                      ofObject:(id)object
-                        change:(NSDictionary*)change
-                       context:(void*)context {
-  DCHECK_EQ(object, self.window);
-  DCHECK([keyPath isEqualToString:@"styleMask"]);
-  [self updateVisualEffectView];
-}
 
 // ThemedWindowDrawing implementation.
 
 - (void)windowDidChangeTheme {
-  [self updateVisualEffectView];
-  [themeBackgroundView_ setNeedsDisplay:YES];
+  [self setNeedsDisplay:YES];
 }
 
 - (void)windowDidChangeActive {
-  // TODO(sdy): It shouldn't be necessary to update the visual effect view
-  // here, since it only changes when the theme changes), but the window isn't
-  // associated with a themeProvider at init time.
-  [self updateVisualEffectView];
-  [themeBackgroundView_ setNeedsDisplay:YES];
+  [self setNeedsDisplay:YES];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view_unittest.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_background_view_unittest.mm
deleted file mode 100644
index 6151556..0000000
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_background_view_unittest.mm
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h"
-
-#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
-#include "ui/base/default_theme_provider.h"
-
-@class NSVisualEffectView;
-
-namespace {
-
-bool ContainsViewOfClass(NSView* view, Class cls) {
-  if ([view isKindOfClass:cls])
-    return true;
-  for (NSView* subview in view.subviews) {
-    if (ContainsViewOfClass(subview, cls))
-      return true;
-  }
-  return false;
-}
-
-class TabStripBackgroundViewTest : public CocoaTest {
- protected:
-  const base::scoped_nsobject<TabStripBackgroundView>
-      tab_strip_background_view_{
-          [[TabStripBackgroundView alloc] initWithFrame:NSZeroRect]};
-};
-
-class MockThemeProvider : public ui::DefaultThemeProvider {
- public:
-  bool UsingSystemTheme() const override { return using_system_theme_; }
-
-  void SetUsingSystemTheme(bool using_system_theme) {
-    using_system_theme_ = using_system_theme;
-  }
-
- private:
-  bool using_system_theme_ = true;
-};
-
-}  // namespace
-
-@interface TabStripBackgroundViewTestWindow : NSWindow
-@property(nonatomic) const ui::ThemeProvider* themeProvider;
-@end
-
-@implementation TabStripBackgroundViewTestWindow
-@synthesize themeProvider = themeProvider_;
-@end
-
-TEST_F(TabStripBackgroundViewTest, TestVisualEffectView) {
-  // TODO(sdy): Remove once we no longer support 10.9.
-  // Skip this test when running on an OS without NSVisualEffectView.
-  if (![NSVisualEffectView class])
-    return;
-
-  auto has_visual_effect_view = [&]() {
-    return ContainsViewOfClass(tab_strip_background_view_,
-                               [NSVisualEffectView class]);
-  };
-
-  EXPECT_FALSE(has_visual_effect_view());
-
-  base::scoped_nsobject<TabStripBackgroundViewTestWindow>
-      scoped_window([[TabStripBackgroundViewTestWindow alloc] init]);
-  TabStripBackgroundViewTestWindow* window = scoped_window.get();
-
-  MockThemeProvider theme_provider;
-  window.themeProvider = &theme_provider;
-
-  [window.contentView addSubview:tab_strip_background_view_];
-
-  [window makeKeyAndOrderFront:nil];
-  [tab_strip_background_view_ windowDidChangeActive];
-  EXPECT_TRUE(has_visual_effect_view());
-
-  window.styleMask |= NSFullScreenWindowMask;
-  EXPECT_FALSE(has_visual_effect_view());
-
-  window.styleMask &= ~NSFullScreenWindowMask;
-  EXPECT_TRUE(has_visual_effect_view());
-
-  theme_provider.SetUsingSystemTheme(false);
-  [tab_strip_background_view_ windowDidChangeTheme];
-  EXPECT_FALSE(has_visual_effect_view());
-
-  theme_provider.SetUsingSystemTheme(true);
-  [tab_strip_background_view_ windowDidChangeTheme];
-  EXPECT_TRUE(has_visual_effect_view());
-}
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
index c533adcc..a9566eaa 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.h
@@ -293,6 +293,10 @@
 
 // Returns the alert state associated with the contents.
 - (TabAlertState)alertStateForContents:(content::WebContents*)contents;
+
+// Leaving visual effects enabled when fullscreen results in higher power
+// consumption. This is used to disable effects when fullscreen.
+- (void)setVisualEffectsDisabledForFullscreen:(BOOL)disabled;
 @end
 
 @interface TabStripController(TestingAPI)
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
index 5eff50bb3..116c0211 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
@@ -2410,4 +2410,8 @@
   }
 }
 
+- (void)setVisualEffectsDisabledForFullscreen:(BOOL)fullscreen {
+  [tabStripView_ setVisualEffectsDisabledForFullscreen:fullscreen];
+}
+
 @end
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_view.h b/chrome/browser/ui/cocoa/tabs/tab_strip_view.h
index 2a570489..55ec16c 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_view.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_view.h
@@ -19,6 +19,8 @@
 
 @interface TabStripView : BackgroundGradientView<URLDropTarget> {
  @private
+  TabStripController* controller_;  // Weak; owns us.
+
   NSTimeInterval lastMouseUp_;
 
   // Handles being a drag-and-drop target.
@@ -30,15 +32,26 @@
   // its tip.
   BOOL dropArrowShown_;
   NSPoint dropArrowPosition_;
+  BOOL inATabDraggingOverlayWindow_;
+  BOOL visualEffectsDisabledForFullscreen_;
 }
 
-@property(assign, nonatomic) TabStripController* controller;
 @property(assign, nonatomic) BOOL dropArrowShown;
 @property(assign, nonatomic) NSPoint dropArrowPosition;
+@property(assign, nonatomic) BOOL inATabDraggingOverlayWindow;
 
 // Name starts with "get" because methods staring with "new" return retained
 // objects according to Cocoa's create rule.
 - (NewTabButton*)getNewTabButton;
+
+// Leaving visual effects enabled when fullscreen results in higher power
+// consumption. This is used to disable effects when fullscreen.
+- (void)setVisualEffectsDisabledForFullscreen:(BOOL)fullscreen;
+@end
+
+// Interface for the controller to set and clear the weak reference to itself.
+@interface TabStripView (TabStripControllerInterface)
+- (void)setController:(TabStripController*)controller;
 @end
 
 // Protected methods subclasses can override to alter behavior. Clients should
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
index 2f430eac7..a59f0204 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm
@@ -12,6 +12,8 @@
 #include "base/mac/sdk_forward_declarations.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
+#import "chrome/browser/ui/cocoa/browser_window_controller.h"
+#import "chrome/browser/ui/cocoa/browser_window_layout.h"
 #import "chrome/browser/ui/cocoa/new_tab_button.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
@@ -27,9 +29,9 @@
 
 @implementation TabStripView
 
-@synthesize controller = controller_;
 @synthesize dropArrowShown = dropArrowShown_;
 @synthesize dropArrowPosition = dropArrowPosition_;
+@synthesize inATabDraggingOverlayWindow = inATabDraggingOverlayWindow_;
 
 - (id)initWithFrame:(NSRect)frame {
   self = [super initWithFrame:frame];
@@ -96,6 +98,43 @@
 }
 
 - (void)drawRect:(NSRect)dirtyRect {
+  const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
+  bool hasCustomThemeImage = themeProvider &&
+      themeProvider->HasCustomImage(IDR_THEME_FRAME);
+  BOOL supportsVibrancy = [self visualEffectView] != nil;
+  BOOL isMainWindow = [[self window] isMainWindow];
+
+  // If in Material Design mode, decrease the tabstrip background's translucency
+  // by overlaying it with a partially-transparent gray (but only if not themed,
+  // and not being used to drag tabs between browser windows). The gray is
+  // somewhat opaque for Incognito mode, very opaque for non-Incognito mode, and
+  // completely opaque when the window is not active.
+  if (themeProvider && !hasCustomThemeImage && !inATabDraggingOverlayWindow_) {
+    NSColor* theColor = nil;
+    if (isMainWindow) {
+      if (supportsVibrancy &&
+          !themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) {
+        theColor = themeProvider->GetNSColor(
+            ThemeProperties::COLOR_FRAME_VIBRANCY_OVERLAY);
+      } else if (!supportsVibrancy && themeProvider->InIncognitoMode()) {
+        theColor = [NSColor colorWithSRGBRed:20 / 255.
+                                       green:22 / 255.
+                                        blue:24 / 255.
+                                       alpha:1];
+      } else {
+        theColor = themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME);
+      }
+    } else {
+      theColor = themeProvider->GetNSColor(
+          ThemeProperties::COLOR_FRAME_INACTIVE);
+    }
+
+    if (theColor) {
+      [theColor set];
+      NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
+    }
+  }
+
   [self drawBottomEdge:dirtyRect];
 
   // Draw drop-indicator arrow (if appropriate).
@@ -308,14 +347,85 @@
   newTabButton_.reset([button retain]);
 }
 
+- (NSVisualEffectView*)visualEffectView {
+  // NSVisualEffectView is only available on OS X 10.10 and higher.
+  if (!base::mac::IsAtLeastOS10_10())
+    return nil;
+
+  NSView* rootView = [[self window] contentView];
+  if (!chrome::ShouldUseFullSizeContentView()) {
+    rootView = [rootView superview];
+  }
+
+  Class nsVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
+  DCHECK(nsVisualEffectViewClass);
+  for (NSView* view in [rootView subviews]) {
+    if ([view isKindOfClass:nsVisualEffectViewClass]) {
+      return base::mac::ObjCCast<NSVisualEffectView>(view);
+    }
+  }
+  return nil;
+}
+
+- (void)setController:(TabStripController*)controller {
+  controller_ = controller;
+  // If tearing down the browser window, there's nothing more to do.
+  if (!controller_) {
+    return;
+  }
+
+  // Finish configuring the NSVisualEffectView so that it matches the window's
+  // theme.
+  NSVisualEffectView* visualEffectView = [self visualEffectView];
+  const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
+  if (!visualEffectView || !themeProvider) {
+    return;
+  }
+
+  // Themes with custom frame images don't use vibrancy. Otherwise, if Incognito
+  // use Material Dark.
+  if (themeProvider->HasCustomImage(IDR_THEME_FRAME) ||
+      themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) {
+    [visualEffectView setState:NSVisualEffectStateInactive];
+  } else if (themeProvider->InIncognitoMode()) {
+    [visualEffectView setMaterial:NSVisualEffectMaterialDark];
+    [visualEffectView setAppearance:
+        [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
+  }
+}
+
 // ThemedWindowDrawing implementation.
 
 - (void)windowDidChangeTheme {
   [self setNeedsDisplay:YES];
+  [self updateVisualEffectState];
 }
 
 - (void)windowDidChangeActive {
   [self setNeedsDisplay:YES];
 }
 
+- (void)setVisualEffectsDisabledForFullscreen:(BOOL)disabled {
+  visualEffectsDisabledForFullscreen_ = disabled;
+  [self updateVisualEffectState];
+}
+
+- (void)updateVisualEffectState {
+  // Configure the NSVisualEffectView so that it does nothing if the user has
+  // switched to a custom theme, or uses vibrancy if the user has switched back
+  // to the default theme.
+  NSVisualEffectView* visualEffectView = [self visualEffectView];
+  const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
+  if (!visualEffectView || !themeProvider) {
+    return;
+  }
+  if (visualEffectsDisabledForFullscreen_ ||
+      themeProvider->HasCustomImage(IDR_THEME_FRAME) ||
+      themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) {
+    [visualEffectView setState:NSVisualEffectStateInactive];
+  } else {
+    [visualEffectView setState:NSVisualEffectStateFollowsWindowActiveState];
+  }
+}
+
 @end
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
index 7267464..e9904d9 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
@@ -18,7 +18,7 @@
 
 @class FastResizeView;
 @class FocusTracker;
-@class TabStripBackgroundView;
+@class NSVisualEffectView;
 @class TabStripView;
 @class TabView;
 
@@ -26,7 +26,10 @@
  @private
   // Wrapper view around web content, and the developer tools view.
   base::scoped_nsobject<FastResizeView> tabContentArea_;
-  base::scoped_nsobject<TabStripBackgroundView> tabStripBackgroundView_;
+  base::scoped_nsobject<NSView> tabStripBackgroundView_;
+
+  // Used to blur the titlebar. nil if window does not have titlebar.
+  base::scoped_nsobject<NSVisualEffectView> visualEffectView_;
 
   // The tab strip overlaps the titlebar of the window.
   base::scoped_nsobject<TabStripView> tabStripView_;
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
index f53ea1d..73d0f3e 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
@@ -16,11 +16,25 @@
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/theme_provider.h"
 
-// TODO(sdy): Remove once we no longer support 10.9 (-Wunguarded-availability).
-@interface NSWindow (TitlebarAppearsTransparent)
-@property BOOL titlebarAppearsTransparent;
+@interface TabWindowController ()
+- (void)setUseOverlay:(BOOL)useOverlay;
+
+// The tab strip background view should always be inserted as the back-most
+// subview of the root view. It cannot be a subview of the contentView, as that
+// would cause it to become layer backed, which would cause it to draw on top
+// of non-layer backed content like the window controls.
+- (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window
+                                      titleBar:(BOOL)hasTitleBar;
+
+// Called when NSWindowWillEnterFullScreenNotification notification received.
+// Makes visual effects view hidden as it should not be displayed in fullscreen.
+- (void)windowWillEnterFullScreenNotification:(NSNotification*)notification;
+
+// Called when NSWindowWillExitFullScreenNotification notification received.
+// Makes visual effects view visible since it was hidden in fullscreen.
+- (void)windowWillExitFullScreenNotification:(NSNotification*)notification;
+
 @end
-// /TODO
 
 @interface TabWindowOverlayWindow : NSWindow
 @end
@@ -71,9 +85,7 @@
     [chromeContentView_
         setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
     [chromeContentView_ setWantsLayer:YES];
-
-    NSView* contentView = self.window.contentView;
-    [contentView addSubview:chromeContentView_];
+    [[[self window] contentView] addSubview:chromeContentView_];
 
     tabContentArea_.reset(
         [[FastResizeView alloc] initWithFrame:[chromeContentView_ bounds]]);
@@ -85,12 +97,14 @@
     // When making a tab dragging window (setUseOverlay:), this view stays in
     // the parent window so that it can be translucent, while the tab strip view
     // moves to the child window and stays opaque.
+    NSView* windowView = [window contentView];
     CGFloat paintHeight = [FramedBrowserWindow browserFrameViewPaintHeight];
     tabStripBackgroundView_.reset([[TabStripBackgroundView alloc]
-        initWithFrame:NSMakeRect(0, NSMaxY([contentView bounds]) - paintHeight,
-                                 NSWidth([contentView bounds]), paintHeight)]);
+        initWithFrame:NSMakeRect(0, NSMaxY([windowView bounds]) - paintHeight,
+                                 NSWidth([windowView bounds]), paintHeight)]);
     [tabStripBackgroundView_
         setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
+    [self insertTabStripBackgroundViewIntoWindow:window titleBar:hasTitleBar];
 
     tabStripView_.reset([[TabStripView alloc]
         initWithFrame:NSMakeRect(
@@ -98,18 +112,25 @@
     [tabStripView_ setAutoresizingMask:NSViewWidthSizable |
                                        NSViewMinYMargin];
     if (hasTabStrip)
-      [contentView addSubview:tabStripView_];
-
-    if ([window respondsToSelector:@selector(setTitlebarAppearsTransparent:)])
-      [window setTitlebarAppearsTransparent:YES];
+      [windowView addSubview:tabStripView_];
 
     if (chrome::ShouldUseFullSizeContentView()) {
-      [contentView addSubview:tabStripBackgroundView_];
-    } else {
-      NSView* rootView = contentView.superview;
-      [rootView addSubview:tabStripBackgroundView_
-                positioned:NSWindowBelow
-                relativeTo:nil];
+      // |windowWillEnterFullScreen:| and |windowWillExitFullScreen:| are
+      // already called because self is a delegate for the window. However this
+      // class is designed for subclassing and can not implement
+      // NSWindowDelegate methods (because subclasses can do so as well and they
+      // should be able to). TODO(crbug.com/654656): Move |visualEffectView_| to
+      // subclass.
+      [[NSNotificationCenter defaultCenter]
+          addObserver:self
+             selector:@selector(windowWillEnterFullScreenNotification:)
+                 name:NSWindowWillEnterFullScreenNotification
+               object:window];
+      [[NSNotificationCenter defaultCenter]
+          addObserver:self
+             selector:@selector(windowWillExitFullScreenNotification:)
+                 name:NSWindowWillExitFullScreenNotification
+               object:window];
     }
   }
   return self;
@@ -189,7 +210,7 @@
     // content view (rather than using setContentView:) because the overlay
     // window has a different content size (due to it being borderless).
     [[overlayWindow_ contentView] addSubview:[self tabStripView]];
-    [tabStripBackgroundView_ setInATabDraggingOverlayWindow:YES];
+    [[self tabStripView] setInATabDraggingOverlayWindow:YES];
     [[overlayWindow_ contentView] addSubview:originalContentView_];
 
     [overlayWindow_ orderFront:nil];
@@ -208,7 +229,7 @@
     [[window contentView] addSubview:[self tabStripView]
                           positioned:NSWindowBelow
                           relativeTo:[self avatarView]];
-    [tabStripBackgroundView_ setInATabDraggingOverlayWindow:NO];
+    [[self tabStripView] setInATabDraggingOverlayWindow:NO];
     [[window contentView] updateTrackingAreas];
 
     [focusBeforeOverlay_ restoreFocusInWindow:window];
@@ -361,10 +382,75 @@
   closeDeferred_ = YES;
 }
 
+- (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window
+                                      titleBar:(BOOL)hasTitleBar {
+  DCHECK(tabStripBackgroundView_);
+  NSView* rootView = [[window contentView] superview];
+
+  // In Material Design on 10.10 and higher, the top portion of the window is
+  // blurred using an NSVisualEffectView.
+  Class nsVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
+  if (!nsVisualEffectViewClass) {
+    DCHECK(!chrome::ShouldUseFullSizeContentView());
+    [rootView addSubview:tabStripBackgroundView_
+              positioned:NSWindowBelow
+              relativeTo:nil];
+    return;
+  }
+
+  [window setTitlebarAppearsTransparent:YES];
+
+  // If the window has a normal titlebar, then do not add NSVisualEffectView.
+  if (hasTitleBar)
+    return;
+
+  visualEffectView_.reset(
+      [[nsVisualEffectViewClass alloc]
+          initWithFrame:[tabStripBackgroundView_ frame]]);
+  DCHECK(visualEffectView_);
+
+  [visualEffectView_ setAutoresizingMask:
+      [tabStripBackgroundView_ autoresizingMask]];
+  [tabStripBackgroundView_
+      setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+  // Set to a default appearance and material. If this is an Incognito window
+  // the material and vibrancy should be dark but this method gets called at
+  // the start of -[BrowserWindowController initWithBrowser:takeOwnership:],
+  // before the |browser_| ivar has been set. Without a browser object we
+  // can't check the window's theme. The final setup happens in
+  // -[TabStripView setController:], at which point we have access to the theme.
+  [visualEffectView_ setAppearance:
+      [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
+  [visualEffectView_ setMaterial:NSVisualEffectMaterialLight];
+  [visualEffectView_ setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+  [visualEffectView_ setState:NSVisualEffectStateFollowsWindowActiveState];
+
+  if (chrome::ShouldUseFullSizeContentView()) {
+    [[window contentView] addSubview:visualEffectView_];
+  } else {
+    [rootView addSubview:visualEffectView_
+              positioned:NSWindowBelow
+              relativeTo:nil];
+  }
+
+  // Make the |tabStripBackgroundView_| a child of the NSVisualEffectView.
+  [tabStripBackgroundView_ setFrame:[visualEffectView_ bounds]];
+  [visualEffectView_ addSubview:tabStripBackgroundView_];
+}
+
 // Called when the size of the window content area has changed. Override to
 // position specific views. Base class implementation does nothing.
 - (void)layoutSubviews {
   NOTIMPLEMENTED();
 }
 
+- (void)windowWillEnterFullScreenNotification:(NSNotification*)notification {
+  [[visualEffectView_ animator] setAlphaValue:0.0];
+}
+
+- (void)windowWillExitFullScreenNotification:(NSNotification*)notification {
+  [[visualEffectView_ animator] setAlphaValue:1.0];
+}
+
 @end
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 7fd6656..fb2ed5a2 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -237,6 +237,11 @@
   }
 }
 
+void CoreOobeHandler::ShowActiveDirectoryPasswordChangeScreen(
+    const std::string& username) {
+  CallJSOrDefer("showActiveDirectoryPasswordChangeScreen", username);
+}
+
 void CoreOobeHandler::ShowSignInUI(const std::string& email) {
   CallJSOrDefer("showSigninUI", email);
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index 30136ad..659f1cc9 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -119,6 +119,8 @@
   void SetClientAreaSize(int width, int height) override;
   void ShowDeviceResetScreen() override;
   void ShowEnableDebuggingScreen() override;
+  void ShowActiveDirectoryPasswordChangeScreen(
+      const std::string& username) override;
 
   void InitDemoModeDetection() override;
   void StopDemoModeDetection() override;
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 03933fc..efacc14 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -416,6 +416,8 @@
               &GaiaScreenHandler::HandleAuthExtensionLoaded);
   AddCallback("completeAdAuthentication",
               &GaiaScreenHandler::HandleCompleteAdAuthentication);
+  AddCallback("completeAdPasswordChange",
+              &GaiaScreenHandler::HandleCompleteAdPasswordChange);
 }
 
 void GaiaScreenHandler::OnPortalDetectionCompleted(
@@ -503,36 +505,74 @@
                                  const Key& key,
                                  authpolicy::ErrorType error,
                                  const std::string& uid) {
-  if (error == authpolicy::ERROR_NONE && !uid.empty()) {
-    const AccountId account_id(
-        GetAccountId(username, uid, AccountType::ACTIVE_DIRECTORY));
-    UserContext user_context(account_id);
-    user_context.SetKey(key);
-    user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
-    user_context.SetIsUsingOAuth(false);
-    user_context.SetUserType(
-        user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
-    Delegate()->CompleteLogin(user_context);
-  } else {
-    // TODO(rsorokin): Proper error handling.
-    DLOG(ERROR) << "Failed to auth " << username << ", code " << error;
-    LoadAuthExtension(true, false /* offline */);
+  switch (error) {
+    case authpolicy::ERROR_NONE: {
+      DCHECK(!uid.empty());
+      const AccountId account_id(
+          GetAccountId(username, uid, AccountType::ACTIVE_DIRECTORY));
+      UserContext user_context(account_id);
+      user_context.SetKey(key);
+      user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
+      user_context.SetIsUsingOAuth(false);
+      user_context.SetUserType(
+          user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
+      Delegate()->CompleteLogin(user_context);
+      break;
+    }
+    case authpolicy::ERROR_PASSWORD_EXPIRED:
+      core_oobe_actor_->ShowActiveDirectoryPasswordChangeScreen(username);
+      break;
+    case authpolicy::ERROR_UNKNOWN:
+    case authpolicy::ERROR_DBUS_FAILURE:
+    case authpolicy::ERROR_PARSE_UPN_FAILED:
+    case authpolicy::ERROR_BAD_USER_NAME:
+    case authpolicy::ERROR_BAD_PASSWORD:
+    case authpolicy::ERROR_CANNOT_RESOLVE_KDC:
+    case authpolicy::ERROR_KINIT_FAILED:
+    case authpolicy::ERROR_NET_FAILED:
+    case authpolicy::ERROR_SMBCLIENT_FAILED:
+    case authpolicy::ERROR_PARSE_FAILED:
+    case authpolicy::ERROR_PARSE_PREG_FAILED:
+    case authpolicy::ERROR_BAD_GPOS:
+    case authpolicy::ERROR_LOCAL_IO:
+    case authpolicy::ERROR_NOT_JOINED:
+    case authpolicy::ERROR_NOT_LOGGED_IN:
+    case authpolicy::ERROR_STORE_POLICY_FAILED:
+      LoadAuthExtension(true, false /* offline */);
+      break;
+    default:
+      // TODO(rsorokin): Proper error handling.
+      DLOG(WARNING) << "Unhandled error code: " << error;
+      LoadAuthExtension(true, false /* offline */);
   }
 }
 
 void GaiaScreenHandler::HandleCompleteAdAuthentication(
-    const std::string& user_name,
+    const std::string& username,
     const std::string& password) {
-  Delegate()->SetDisplayEmail(user_name);
-  set_populated_email(user_name);
+  Delegate()->SetDisplayEmail(username);
+  set_populated_email(username);
 
   login::GetPipeReadEnd(
       password,
       base::Bind(&GaiaScreenHandler::OnPasswordPipeReady,
-                 weak_factory_.GetWeakPtr(), user_name, Key(password)));
+                 weak_factory_.GetWeakPtr(), username, Key(password)));
 }
 
-void GaiaScreenHandler::OnPasswordPipeReady(const std::string& user_name,
+void GaiaScreenHandler::HandleCompleteAdPasswordChange(
+    const std::string& username,
+    const std::string& old_password,
+    const std::string& new_password) {
+  Delegate()->SetDisplayEmail(username);
+  set_populated_email(username);
+
+  login::GetPipeReadEnd(
+      old_password + "\n" + new_password + "\n" + new_password,
+      base::Bind(&GaiaScreenHandler::OnPasswordPipeReady,
+                 weak_factory_.GetWeakPtr(), username, Key(new_password)));
+}
+
+void GaiaScreenHandler::OnPasswordPipeReady(const std::string& username,
                                             const Key& key,
                                             base::ScopedFD password_fd) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -543,9 +583,9 @@
   chromeos::AuthPolicyClient* client =
       chromeos::DBusThreadManager::Get()->GetAuthPolicyClient();
   client->AuthenticateUser(
-      user_name, password_fd.get(),
+      username, password_fd.get(),
       base::Bind(&GaiaScreenHandler::DoAdAuth, weak_factory_.GetWeakPtr(),
-                 user_name, key));
+                 username, key));
 }
 
 void GaiaScreenHandler::HandleCompleteAuthentication(
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index a3b8da0..a81b462 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -98,9 +98,13 @@
                            const std::string& password,
                            bool using_saml);
 
-  void HandleCompleteAdAuthentication(const std::string& user_name,
+  void HandleCompleteAdAuthentication(const std::string& username,
                                       const std::string& password);
 
+  void HandleCompleteAdPasswordChange(const std::string& username,
+                                      const std::string& old_password,
+                                      const std::string& new_password);
+
   void HandleUsingSAMLAPI();
   void HandleScrapedPasswordCount(int password_count);
   void HandleScrapedPasswordVerificationFailed();
@@ -139,7 +143,7 @@
                 const std::string& uid);
 
   // Callback for writing password into pipe.
-  void OnPasswordPipeReady(const std::string& user_name,
+  void OnPasswordPipeReady(const std::string& username,
                            const Key& key,
                            base::ScopedFD password_fd);
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 50d3396..3b893b2 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -518,6 +518,14 @@
                IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_CONTINUE);
   builder->Add("unrecoverableCryptohomeErrorRecreatingProfile",
                IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_WAIT_MESSAGE);
+
+  builder->Add("adEnterOldPasswordHint", IDS_AD_PASSWORD_CHANGE_OLD_PASSWORD);
+  builder->Add("adEnterNewPasswordHint", IDS_AD_PASSWORD_CHANGE_NEW_PASSWORD);
+  builder->Add("adRepeatNewPasswordHint",
+               IDS_AD_PASSWORD_CHANGE_REPEAT_NEW_PASSWORD);
+  builder->Add("adPasswordChangeMessage", IDS_AD_PASSWORD_CHANGE_MESSAGE);
+  builder->Add("adOldPasswordError", IDS_AD_PASSWORD_CHANGE_INVALID_PASSWORD);
+  builder->Add("adNewPasswordError", IDS_AD_PASSWORD_CHANGE_PASSWORDS_MISMATCH);
 }
 
 void SigninScreenHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
index 5564716..e12782d 100644
--- a/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
+++ b/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -42,6 +42,7 @@
   void RegisterMessages() override;
 
   // ntp_tiles::NTPTilesInternalsMessageHandlerClient
+  bool SupportsNTPTiles() override;
   bool DoesSourceExist(ntp_tiles::NTPTileSource source) override;
   std::unique_ptr<ntp_tiles::MostVisitedSites> MakeMostVisitedSites() override;
   std::unique_ptr<ntp_tiles::PopularSites> MakePopularSites() override;
@@ -62,6 +63,11 @@
   handler_.RegisterMessages(this);
 }
 
+bool ChromeNTPTilesInternalsMessageHandlerClient::SupportsNTPTiles() {
+  Profile* profile = Profile::FromWebUI(web_ui());
+  return !(profile->IsGuestSession() || profile->IsOffTheRecord());
+}
+
 bool ChromeNTPTilesInternalsMessageHandlerClient::DoesSourceExist(
     ntp_tiles::NTPTileSource source) {
   switch (source) {
diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
index bd3836f..32ed66b 100644
--- a/chrome/renderer/chrome_render_thread_observer.cc
+++ b/chrome/renderer/chrome_render_thread_observer.cc
@@ -279,9 +279,9 @@
 
 void ChromeRenderThreadObserver::RegisterMojoInterfaces(
     content::AssociatedInterfaceRegistry* associated_interfaces) {
-  associated_interfaces->AddInterface(
-      base::Bind(&ChromeRenderThreadObserver::OnRendererInterfaceRequest,
-                 base::Unretained(this)));
+  associated_interfaces->AddInterface(base::Bind(
+      &ChromeRenderThreadObserver::OnRendererConfigurationAssociatedRequest,
+      base::Unretained(this)));
 }
 
 void ChromeRenderThreadObserver::UnregisterMojoInterfaces(
@@ -331,7 +331,7 @@
   content_setting_rules_ = rules;
 }
 
-void ChromeRenderThreadObserver::OnRendererInterfaceRequest(
+void ChromeRenderThreadObserver::OnRendererConfigurationAssociatedRequest(
     chrome::mojom::RendererConfigurationAssociatedRequest request) {
   DCHECK(!renderer_configuration_binding_.is_bound());
   renderer_configuration_binding_.Bind(std::move(request));
diff --git a/chrome/renderer/chrome_render_thread_observer.h b/chrome/renderer/chrome_render_thread_observer.h
index d8ed9a1..91540065 100644
--- a/chrome/renderer/chrome_render_thread_observer.h
+++ b/chrome/renderer/chrome_render_thread_observer.h
@@ -65,7 +65,7 @@
   void SetContentSettingRules(
       const RendererContentSettingRules& rules) override;
 
-  void OnRendererInterfaceRequest(
+  void OnRendererConfigurationAssociatedRequest(
       chrome::mojom::RendererConfigurationAssociatedRequest request);
 
   void OnSetFieldTrialGroup(const std::string& trial_name,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cfaf9d82..e0cfab3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4620,7 +4620,6 @@
         "../browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm",
         "../browser/ui/cocoa/tabs/alert_indicator_button_cocoa_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_controller_unittest.mm",
-        "../browser/ui/cocoa/tabs/tab_strip_background_view_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_strip_controller_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_strip_view_unittest.mm",
         "../browser/ui/cocoa/tabs/tab_view_unittest.mm",
diff --git a/components/cronet/android/cronet_impl_native_proguard.cfg b/components/cronet/android/cronet_impl_native_proguard.cfg
index c0493225..383cc049 100644
--- a/components/cronet/android/cronet_impl_native_proguard.cfg
+++ b/components/cronet/android/cronet_impl_native_proguard.cfg
@@ -1,7 +1,6 @@
 # Proguard config for apps that depend on cronet_impl_native_java.jar.
 
 -keep class org.chromium.net.impl.CronetUrlRequest$HeadersList
--keep class org.chromium.net.impl.ChromiumUrlRequest$ResponseHeadersMap
 
 # Suppress unnecessary warnings.
 -dontnote org.chromium.net.ProxyChangeListener$ProxyReceiver
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
index 4dce944..9f876ee 100644
--- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
+++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
@@ -44,6 +44,9 @@
 
 void NTPTilesInternalsMessageHandler::HandleRegisterForEvents(
     const base::ListValue* args) {
+  if (!client_->SupportsNTPTiles()) {
+    return;
+  }
   DCHECK(args->empty());
 
   SendSourceInfo();
@@ -54,6 +57,9 @@
 
 void NTPTilesInternalsMessageHandler::HandleUpdate(
     const base::ListValue* args) {
+  if (!client_->SupportsNTPTiles()) {
+    return;
+  }
   const base::DictionaryValue* dict = nullptr;
   DCHECK_EQ(1u, args->GetSize());
   args->GetDictionary(0, &dict);
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h
index 4a917008..d26972d 100644
--- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h
+++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h
@@ -31,6 +31,9 @@
   // Returns the PrefService for the embedder and containing WebUI page.
   virtual PrefService* GetPrefs() = 0;
 
+  // False if in a browser mode (e.g. incognito) where tiles aren't supported.
+  virtual bool SupportsNTPTiles() = 0;
+
   // Returns true if the given source is enabled (even if, in practice, none of
   // the tiles would come from it).
   virtual bool DoesSourceExist(NTPTileSource source) = 0;
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 503a718..c76864f 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -52,6 +52,8 @@
     "form_saver.h",
     "form_saver_impl.cc",
     "form_saver_impl.h",
+    "http_password_migrator.cc",
+    "http_password_migrator.h",
     "import/csv_reader.cc",
     "import/csv_reader.h",
     "import/password_csv_reader.cc",
@@ -271,6 +273,7 @@
     "facet_manager_unittest.cc",
     "form_fetcher_impl_unittest.cc",
     "form_saver_impl_unittest.cc",
+    "http_password_migrator_unittest.cc",
     "import/csv_reader_unittest.cc",
     "import/password_csv_reader_unittest.cc",
     "import/password_importer_unittest.cc",
diff --git a/components/password_manager/core/browser/http_password_migrator.cc b/components/password_manager/core/browser/http_password_migrator.cc
new file mode 100644
index 0000000..9f331071
--- /dev/null
+++ b/components/password_manager/core/browser/http_password_migrator.cc
@@ -0,0 +1,60 @@
+// 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 "components/password_manager/core/browser/http_password_migrator.h"
+
+#include "components/password_manager/core/browser/password_store.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+namespace password_manager {
+
+HttpPasswordMigrator::HttpPasswordMigrator(const GURL& https_origin,
+                                           PasswordStore* password_store,
+                                           Consumer* consumer)
+    : consumer_(consumer), password_store_(password_store) {
+  DCHECK(password_store_);
+  DCHECK(https_origin.is_valid());
+  DCHECK(https_origin.SchemeIs(url::kHttpsScheme)) << https_origin;
+
+  GURL::Replacements rep;
+  rep.SetSchemeStr(url::kHttpScheme);
+  GURL http_origin = https_origin.ReplaceComponents(rep);
+  PasswordStore::FormDigest form(autofill::PasswordForm::SCHEME_HTML,
+                                 http_origin.GetOrigin().spec(), http_origin);
+  password_store_->GetLogins(form, this);
+}
+
+HttpPasswordMigrator::~HttpPasswordMigrator() = default;
+
+void HttpPasswordMigrator::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+  // Android and PSL matches are ignored.
+  results.erase(
+      std::remove_if(results.begin(), results.end(),
+                     [](const std::unique_ptr<autofill::PasswordForm>& form) {
+                       return form->is_affiliation_based_match ||
+                              form->is_public_suffix_match;
+                     }),
+      results.end());
+
+  // Add the new credentials to the password store. The HTTP forms aren't
+  // removed for now.
+  for (const auto& form : results) {
+    GURL::Replacements rep;
+    rep.SetSchemeStr(url::kHttpsScheme);
+    form->origin = form->origin.ReplaceComponents(rep);
+    form->signon_realm = form->origin.spec();
+    form->action = form->origin;
+    form->form_data = autofill::FormData();
+    form->generation_upload_status = autofill::PasswordForm::NO_SIGNAL_SENT;
+    form->skip_zero_click = false;
+    password_store_->AddLogin(*form);
+  }
+
+  if (consumer_)
+    consumer_->ProcessMigratedForms(std::move(results));
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/http_password_migrator.h b/components/password_manager/core/browser/http_password_migrator.h
new file mode 100644
index 0000000..cf054487
--- /dev/null
+++ b/components/password_manager/core/browser/http_password_migrator.h
@@ -0,0 +1,58 @@
+// 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 COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace autofill {
+struct PasswordForm;
+}
+
+class GURL;
+
+namespace password_manager {
+
+class PasswordStore;
+
+// The class is responsible for migrating the passwords saved on HTTP to HTTPS
+// origin.
+class HttpPasswordMigrator : public PasswordStoreConsumer {
+ public:
+  // API to be implemented by an embedder of HttpPasswordMigrator.
+  class Consumer {
+   public:
+    virtual ~Consumer() = default;
+
+    // Notify the embedder that |forms| were migrated to HTTPS. |forms| contain
+    // the updated HTTPS scheme.
+    virtual void ProcessMigratedForms(
+        std::vector<std::unique_ptr<autofill::PasswordForm>> forms) = 0;
+  };
+
+  // |https_origin| should specify a valid HTTPS URL.
+  HttpPasswordMigrator(const GURL& https_origin,
+                       PasswordStore* password_store,
+                       Consumer* consumer);
+  ~HttpPasswordMigrator() override;
+
+  // PasswordStoreConsumer:
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+ private:
+  Consumer* consumer_;
+  PasswordStore* password_store_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpPasswordMigrator);
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_HTTP_PASSWORD_MIGRATOR_H_
diff --git a/components/password_manager/core/browser/http_password_migrator_unittest.cc b/components/password_manager/core/browser/http_password_migrator_unittest.cc
new file mode 100644
index 0000000..ab7bb55
--- /dev/null
+++ b/components/password_manager/core/browser/http_password_migrator_unittest.cc
@@ -0,0 +1,131 @@
+// 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 "components/password_manager/core/browser/http_password_migrator.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+using autofill::PasswordForm;
+using testing::_;
+using testing::ElementsAre;
+using testing::Pointee;
+
+constexpr char kTestHttpsURL[] = "https://example.org/";
+constexpr char kTestHttpURL[] = "http://example.org/";
+constexpr char kTestSubdomainHttpURL[] = "http://login.example.org/";
+
+// Creates a dummy http form with some basic arbitrary values.
+PasswordForm CreateTestForm() {
+  PasswordForm form;
+  form.origin = GURL(kTestHttpURL);
+  form.signon_realm = form.origin.spec();
+  form.action = GURL(kTestHttpURL);
+  form.username_value = base::ASCIIToUTF16("user");
+  form.password_value = base::ASCIIToUTF16("password");
+  return form;
+}
+
+// Creates a dummy http PSL-matching form with some basic arbitrary values.
+PasswordForm CreateTestPSLForm() {
+  PasswordForm form;
+  form.origin = GURL(kTestSubdomainHttpURL);
+  form.signon_realm = form.origin.spec();
+  form.action = GURL(kTestSubdomainHttpURL);
+  form.username_value = base::ASCIIToUTF16("user2");
+  form.password_value = base::ASCIIToUTF16("password2");
+  form.is_public_suffix_match = true;
+  return form;
+}
+
+// Creates an Android credential.
+PasswordForm CreateAndroidCredential() {
+  PasswordForm form;
+  form.username_value = base::ASCIIToUTF16("user3");
+  form.password_value = base::ASCIIToUTF16("password3");
+  form.signon_realm = "android://hash@com.example.android/";
+  form.origin = GURL(form.signon_realm);
+  form.action = GURL();
+  form.is_affiliation_based_match = true;
+  return form;
+}
+
+class MockConsumer : public HttpPasswordMigrator::Consumer {
+ public:
+  MOCK_METHOD1(ProcessForms,
+               void(const std::vector<autofill::PasswordForm*>& forms));
+
+  void ProcessMigratedForms(
+      std::vector<std::unique_ptr<autofill::PasswordForm>> forms) override {
+    std::vector<autofill::PasswordForm*> raw_forms(forms.size());
+    std::transform(forms.begin(), forms.end(), raw_forms.begin(),
+                   [](const std::unique_ptr<autofill::PasswordForm>& form) {
+                     return form.get();
+                   });
+    ProcessForms(raw_forms);
+  }
+};
+
+class HttpPasswordMigratorTest : public testing::Test {
+ public:
+  HttpPasswordMigratorTest() {
+    mock_store_ = new testing::StrictMock<MockPasswordStore>;
+  }
+
+  ~HttpPasswordMigratorTest() override { mock_store_->ShutdownOnUIThread(); }
+
+  MockConsumer& consumer() { return consumer_; }
+  MockPasswordStore& store() { return *mock_store_; }
+
+ private:
+  base::MessageLoop message_loop_;  // Used by mock_store_.
+  MockConsumer consumer_;
+  scoped_refptr<MockPasswordStore> mock_store_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpPasswordMigratorTest);
+};
+
+TEST_F(HttpPasswordMigratorTest, EmptyStore) {
+  PasswordStore::FormDigest form(autofill::PasswordForm::SCHEME_HTML,
+                                 kTestHttpURL, GURL(kTestHttpURL));
+  EXPECT_CALL(store(), GetLogins(form, _));
+  HttpPasswordMigrator migrator(GURL(kTestHttpsURL), &store(), &consumer());
+
+  EXPECT_CALL(consumer(), ProcessForms(std::vector<autofill::PasswordForm*>()));
+  migrator.OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<autofill::PasswordForm>>());
+}
+
+TEST_F(HttpPasswordMigratorTest, FullStore) {
+  PasswordStore::FormDigest form_digest(autofill::PasswordForm::SCHEME_HTML,
+                                        kTestHttpURL, GURL(kTestHttpURL));
+  EXPECT_CALL(store(), GetLogins(form_digest, _));
+  HttpPasswordMigrator migrator(GURL(kTestHttpsURL), &store(), &consumer());
+
+  PasswordForm form = CreateTestForm();
+  PasswordForm psl_form = CreateTestPSLForm();
+  PasswordForm android_form = CreateAndroidCredential();
+  PasswordForm expected_form = form;
+  expected_form.origin = GURL(kTestHttpsURL);
+  expected_form.signon_realm = expected_form.origin.spec();
+  expected_form.action = expected_form.origin;
+
+  EXPECT_CALL(store(), AddLogin(expected_form));
+  EXPECT_CALL(consumer(), ProcessForms(ElementsAre(Pointee(expected_form))));
+  std::vector<std::unique_ptr<autofill::PasswordForm>> results;
+  results.push_back(base::MakeUnique<PasswordForm>(psl_form));
+  results.push_back(base::MakeUnique<PasswordForm>(form));
+  results.push_back(base::MakeUnique<PasswordForm>(android_form));
+  migrator.OnGetPasswordStoreResults(std::move(results));
+}
+
+}  // namespace
+}  // namespace password_manager
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc
index 466ae9d5..cc5d7662 100644
--- a/content/renderer/media/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/media_stream_audio_processor_options.cc
@@ -467,6 +467,8 @@
   stats->echo_delay_std_ms = apm_stats.delay_standard_deviation;
 
   stats->residual_echo_likelihood = apm_stats.residual_echo_likelihood;
+  stats->residual_echo_likelihood_recent_max =
+      apm_stats.residual_echo_likelihood_recent_max;
 }
 
 std::vector<webrtc::Point> GetArrayGeometryPreferringConstraints(
diff --git a/ios/BUILD.gn b/ios/BUILD.gn
index 2b7e97d..79a7f39 100644
--- a/ios/BUILD.gn
+++ b/ios/BUILD.gn
@@ -41,11 +41,6 @@
       "//ios/testing:all_tests",
       "//ios/web:all_tests",
       "//ios/web/shell/test:all_tests",
-
-      # Those dependencies are currently only used from downstream code
-      # and will be removed once the dependent code has been upstreamed.
-      "//ios/chrome/share_extension",
-      "//ios/chrome/today_extension",
     ]
   }
 }
diff --git a/ios/chrome/browser/google/BUILD.gn b/ios/chrome/browser/google/BUILD.gn
index d7b940b..7750824 100644
--- a/ios/chrome/browser/google/BUILD.gn
+++ b/ios/chrome/browser/google/BUILD.gn
@@ -19,6 +19,7 @@
     "//components/prefs",
     "//ios/chrome/browser/browser_state",
     "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/distribution",
   ]
 }
 
diff --git a/ios/chrome/browser/google/google_brand.mm b/ios/chrome/browser/google/google_brand.mm
index df5ae652..1405fe5 100644
--- a/ios/chrome/browser/google/google_brand.mm
+++ b/ios/chrome/browser/google/google_brand.mm
@@ -5,6 +5,7 @@
 #include "ios/chrome/browser/google/google_brand.h"
 
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#include "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -17,7 +18,9 @@
   if (!ios::GetChromeBrowserProvider())
     return false;
 
-  brand->assign(ios::GetChromeBrowserProvider()->GetDistributionBrandCode());
+  brand->assign(ios::GetChromeBrowserProvider()
+                    ->GetAppDistributionProvider()
+                    ->GetDistributionBrandCode());
   return true;
 }
 
diff --git a/ios/chrome/browser/net/BUILD.gn b/ios/chrome/browser/net/BUILD.gn
index b628ccce..2d147eb 100644
--- a/ios/chrome/browser/net/BUILD.gn
+++ b/ios/chrome/browser/net/BUILD.gn
@@ -21,10 +21,6 @@
     "ios_chrome_network_delegate.h",
     "ios_chrome_url_request_context_getter.cc",
     "ios_chrome_url_request_context_getter.h",
-    "metrics_network_client.h",
-    "metrics_network_client.mm",
-    "metrics_network_client_manager.h",
-    "metrics_network_client_manager.mm",
     "net_types.h",
     "proxy_service_factory.cc",
     "proxy_service_factory.h",
@@ -59,7 +55,6 @@
   testonly = true
   sources = [
     "cookie_util_unittest.mm",
-    "metrics_network_client_unittest.mm",
     "retryable_url_fetcher_unittest.mm",
   ]
   deps = [
diff --git a/ios/chrome/browser/net/metrics_network_client.h b/ios/chrome/browser/net/metrics_network_client.h
deleted file mode 100644
index 90b37fb..0000000
--- a/ios/chrome/browser/net/metrics_network_client.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
-#define IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
-
-#import "ios/net/clients/crn_forwarding_network_client.h"
-
-@class MetricsNetworkClientManager;
-
-// MetricsNetworkClient records UMA metrics about the network requests.
-@interface MetricsNetworkClient : CRNForwardingNetworkClient
-
-- (instancetype)initWithManager:(MetricsNetworkClientManager*)manager;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_H_
diff --git a/ios/chrome/browser/net/metrics_network_client.mm b/ios/chrome/browser/net/metrics_network_client.mm
deleted file mode 100644
index 1ac8e73..0000000
--- a/ios/chrome/browser/net/metrics_network_client.mm
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/net/metrics_network_client.h"
-
-#import "base/ios/weak_nsobject.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/net/metrics_network_client_manager.h"
-#include "ios/web/public/url_util.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_response_headers.h"
-#include "net/url_request/url_request.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface MetricsNetworkClient () {
-  BOOL _histogramUpdated;
-  // Pointer to the load time record for this request. This will be created
-  // and owned by |_manager|, and it will remain valid as long as |_manager| is.
-  __unsafe_unretained PageLoadTimeRecord* _loadTimeRecord;
-  // Pointer to the creating manager, which is owned by a tab. All network
-  // requests for the tab are destroyed before the tab is, so this pointer
-  // will always be valid as long as the owning client is alive.
-  __unsafe_unretained MetricsNetworkClientManager* _manager;
-  // A pointer to the request, kept so it can be referred to later.
-  scoped_refptr<net::HttpResponseHeaders> _nativeHeaders;
-}
-- (void)updateHistogram:(NSInteger)code;
-@end
-
-@implementation MetricsNetworkClient
-
-- (void)updateHistogram:(NSInteger)code {
-  DCHECK(!_histogramUpdated) << "Histogram should not be updated twice.";
-  // The |error| must be in the |net::kErrorDomain|. All those errors are
-  // defined in |net/base/net_error_list.h|, must be negative values that
-  // fits in an |int|. See |net/base/net_errors.h| for more information.
-  DCHECK_LE(code, 0) << "Net error codes should be negative.";
-  DCHECK_GT(code, INT_MIN) << "Net error code should fit in an int.";
-  // On iOS, we cannot distinguish between main frames, images and other
-  // subresources. Consequently, all the codes are aggregated in
-  // |ErrorCodesForMainFrame3|.
-  // The other histograms (such as |ErrorCodesForHTTPSGoogleMainFrame2|,
-  // |ErrorCodesForImages| and |ErrorCodesForSubresources2|) are not filled.
-  UMA_HISTOGRAM_SPARSE_SLOWLY("Net.ErrorCodesForMainFrame3",
-                              static_cast<int>(-code));
-  _histogramUpdated = YES;
-}
-
-- (instancetype)initWithManager:(MetricsNetworkClientManager*)manager {
-  if ((self = [super init])) {
-    _manager = manager;
-  }
-  return self;
-}
-
-#pragma mark CRNNetworkClientProtocol methods
-
-- (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
-  [super didCreateNativeRequest:nativeRequest];
-  GURL url = web::GURLByRemovingRefFromGURL(nativeRequest->original_url());
-  _loadTimeRecord =
-      [_manager recordForPageLoad:url time:nativeRequest->creation_time()];
-  _nativeHeaders = nativeRequest->response_headers();
-}
-
-- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
-                  netErrorCode:(int)netErrorCode {
-  [super didFailWithNSErrorCode:nsErrorCode netErrorCode:netErrorCode];
-
-  // Ignore NSURLErrorCancelled errors, which are sometimes created to
-  // silently abort loads.
-
-  if (nsErrorCode != NSURLErrorCancelled) {
-    [self updateHistogram:netErrorCode];
-  }
-}
-
-- (void)didFinishLoading {
-  [super didFinishLoading];
-  [self updateHistogram:net::OK];
-}
-
-@end
diff --git a/ios/chrome/browser/net/metrics_network_client_manager.h b/ios/chrome/browser/net/metrics_network_client_manager.h
deleted file mode 100644
index dc0c43b..0000000
--- a/ios/chrome/browser/net/metrics_network_client_manager.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
-#define IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
-
-#import <Foundation/Foundation.h>
-
-#import "base/time/time.h"
-#import "ios/net/clients/crn_forwarding_network_client_factory.h"
-#include "url/gurl.h"
-
-@interface PageLoadTimeRecord : NSObject
-@end
-
-// Factory that creates MetricsNetworkClient instances.
-// Each Tab that cares to report page load metrics should create an instance
-// of this class add add it to its request tracker.
-@interface MetricsNetworkClientManager : CRNForwardingNetworkClientFactory
-
-// Called by the tab when a new page load is about to start, to signal the
-// current page URL.
-- (void)pageLoadStarted:(GURL)url;
-
-// Called by the tab when the page load is complete.
-- (void)pageLoadCompleted;
-
-// Return a page load time record that will be used to record a load of |url|
-// that started at |time|. Returns nil if |url| doesn't match the current page
-// url. The page load record is owned by the target, and the caller should not
-// take ownership of it.
-// This method should only be called on the IO thread.
-- (PageLoadTimeRecord*)recordForPageLoad:(const GURL&)url
-                                    time:(base::TimeTicks)time;
-@end
-
-#endif  // IOS_CHROME_BROWSER_NET_METRICS_NETWORK_CLIENT_MANAGER_H_
diff --git a/ios/chrome/browser/net/metrics_network_client_manager.mm b/ios/chrome/browser/net/metrics_network_client_manager.mm
deleted file mode 100644
index 38ee975..0000000
--- a/ios/chrome/browser/net/metrics_network_client_manager.mm
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/net/metrics_network_client_manager.h"
-
-#import "base/ios/weak_nsobject.h"
-#import "base/location.h"
-#include "base/mac/bind_objc_block.h"
-#include "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram.h"
-#import "ios/chrome/browser/net/metrics_network_client.h"
-#include "ios/web/public/web_thread.h"
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface PageLoadTimeRecord ()
-
-@property(nonatomic, readonly) GURL url;
-@property(nonatomic, readonly) base::TimeTicks creationTime;
-@property(nonatomic, assign) BOOL alreadyCounted;
-
-- (instancetype)initWithURL:(const GURL&)url time:(base::TimeTicks)time;
-
-@end
-
-@implementation PageLoadTimeRecord {
-  GURL _url;
-  base::TimeTicks _creationTime;
-  BOOL _alreadyCounted;
-}
-
-@synthesize url = _url;
-@synthesize creationTime = _creationTime;
-@synthesize alreadyCounted = _alreadyCounted;
-
-- (instancetype)initWithURL:(const GURL&)url time:(base::TimeTicks)time {
-  if ((self = [super init])) {
-    _url = url;
-    _creationTime = time;
-  }
-  return self;
-}
-
-@end
-
-@interface MetricsNetworkClientManager ()
-
-// IO-thread-only methods.
-- (void)handlePageLoadStarted:(const GURL&)url;
-- (void)handlePageLoadCompleted;
-
-@end
-
-@implementation MetricsNetworkClientManager {
-  // Set of page load time objects created. Beyond deallocation and
-  // creation, should only be accessed on the IO thread.
-  base::scoped_nsobject<NSMutableSet> _pageLoadTimes;
-  // Current URL being loaded by the tab that owns this object. Only accessible
-  // on the IO thread.
-  GURL _pageURL;
-}
-
-- (instancetype)init {
-  if ((self = [super init])) {
-    _pageLoadTimes.reset([NSMutableSet set]);
-  }
-  return self;
-}
-
-- (Class)clientClass {
-  return [MetricsNetworkClient class];
-}
-
-#pragma mark CRWForwardingNetworkClientFactory methods
-
-- (CRNForwardingNetworkClient*)clientHandlingRequest:
-        (const net::URLRequest&)request {
-  return [[MetricsNetworkClient alloc] initWithManager:self];
-}
-
-#pragma mark - public UI-thread methods
-
-- (void)pageLoadStarted:(GURL)url {
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlockArc(^{
-                             [self handlePageLoadStarted:url];
-                           }));
-}
-
-- (void)pageLoadCompleted {
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
-  web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlockArc(^{
-                             [self handlePageLoadCompleted];
-                           }));
-}
-
-#pragma mark - public IO-thread methods
-
-- (PageLoadTimeRecord*)recordForPageLoad:(const GURL&)url
-                                    time:(base::TimeTicks)time {
-  DCHECK_CURRENTLY_ON(web::WebThread::IO);
-  base::scoped_nsobject<PageLoadTimeRecord> plt;
-  if (!_pageURL.spec().empty() && url == _pageURL) {
-    plt.reset([[PageLoadTimeRecord alloc] initWithURL:url time:time]);
-    [_pageLoadTimes addObject:plt];
-  }
-  return plt.get();
-}
-
-#pragma mark - IO-thread handlers for UI thread methods.
-
-- (void)handlePageLoadStarted:(const GURL&)url {
-  DCHECK_CURRENTLY_ON(web::WebThread::IO);
-  [_pageLoadTimes removeAllObjects];
-  _pageURL = url;
-}
-
-- (void)handlePageLoadCompleted {
-  DCHECK_CURRENTLY_ON(web::WebThread::IO);
-  for (PageLoadTimeRecord* plt in _pageLoadTimes.get()) {
-    if (plt.url == _pageURL && !plt.alreadyCounted) {
-      plt.alreadyCounted = YES;
-      base::TimeDelta elapsed = base::TimeTicks::Now() - plt.creationTime;
-      UMA_HISTOGRAM_MEDIUM_TIMES("Tabs.iOS_PostRedirectPLT", elapsed);
-      break;
-    }
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/net/metrics_network_client_unittest.mm b/ios/chrome/browser/net/metrics_network_client_unittest.mm
deleted file mode 100644
index ed93c28a..0000000
--- a/ios/chrome/browser/net/metrics_network_client_unittest.mm
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/net/metrics_network_client.h"
-
-#include "base/mac/scoped_nsobject.h"
-#include "base/test/histogram_tester.h"
-#include "net/base/net_errors.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Dummy client to be registered as underlying client for the
-// MetricsNetworkClient.
-@interface MetricsMockClient : CRNForwardingNetworkClient
-@end
-
-@implementation MetricsMockClient
-
-- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
-                  netErrorCode:(int)netErrorCode {
-}
-
-- (void)didFinishLoading {
-}
-
-@end
-
-namespace {
-
-// Name for the histogram the MetricsNetworkClient has to update.
-const char kHistogramName[] = "Net.ErrorCodesForMainFrame3";
-
-class MetricsNetworkClientTest : public testing::Test {
- public:
-  MetricsNetworkClientTest()
-      : histogram_tester_(), client_([[MetricsNetworkClient alloc] init]) {
-    // Setup a dummy underlying client to avoid DCHECKs.
-    base::scoped_nsobject<MetricsMockClient> underying_client(
-        [[MetricsMockClient alloc] init]);
-    [client_ setUnderlyingClient:underying_client];
-  }
-
-  // Returns true if there are no samples for "Net.ErrorCodesForMainFrame3".
-  void VerifyNoSamples() {
-    histogram_tester_.ExpectTotalCount(kHistogramName, 0);
-  }
-
- protected:
-  base::HistogramTester histogram_tester_;
-  base::scoped_nsobject<MetricsNetworkClient> client_;
-};
-
-}  // namespace
-
-TEST_F(MetricsNetworkClientTest, HistogramUpdatedOnErrors) {
-  int net_error = net::ERR_FAILED;
-  VerifyNoSamples();
-  // NSURLErrorCancelled errors must not update the histogram.
-  [client_ didFailWithNSErrorCode:NSURLErrorCancelled netErrorCode:net_error];
-  VerifyNoSamples();
-  // Other iOS errors update the histogram.
-  [client_ didFailWithNSErrorCode:NSURLErrorCannotConnectToHost
-                     netErrorCode:net_error];
-  // |net_error| is negative, the histogram reports the opposite value.
-  histogram_tester_.ExpectUniqueSample(kHistogramName, -net_error, 1);
-}
-
-TEST_F(MetricsNetworkClientTest, HistogramUpdatedOnSuccess) {
-  VerifyNoSamples();
-  [client_ didFinishLoading];
-  histogram_tester_.ExpectUniqueSample(kHistogramName, -net::OK, 1);
-}
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.h b/ios/chrome/browser/providers/chromium_browser_provider.h
index cc5c31b1..ddd0e00 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.h
+++ b/ios/chrome/browser/providers/chromium_browser_provider.h
@@ -20,8 +20,6 @@
   ios::ChromeIdentityService* GetChromeIdentityService() override;
   UITextField<TextFieldStyling>* CreateStyledTextField(
       CGRect frame) const override NS_RETURNS_RETAINED;
-  void InitializeCastService(id main_tab_model) const override;
-  void AttachTabHelpers(web::WebState* web_state, id tab) const override;
   VoiceSearchProvider* GetVoiceSearchProvider() const override;
   id<LogoVendor> CreateLogoVendor(ios::ChromeBrowserState* browser_state,
                                   id<UrlLoader> loader) const override
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.mm b/ios/chrome/browser/providers/chromium_browser_provider.mm
index 3d5ed08..ab35120 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.mm
+++ b/ios/chrome/browser/providers/chromium_browser_provider.mm
@@ -59,11 +59,6 @@
   return [[ChromiumStyledTextField alloc] initWithFrame:CGRectZero];
 }
 
-void ChromiumBrowserProvider::InitializeCastService(id main_tab_model) const {}
-
-void ChromiumBrowserProvider::AttachTabHelpers(web::WebState* web_state,
-                                               id tab) const {}
-
 VoiceSearchProvider* ChromiumBrowserProvider::GetVoiceSearchProvider() const {
   return voice_search_provider_.get();
 }
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index 8a5b6e5..3ac08a4 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -74,7 +74,6 @@
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/native_app_launcher:native_app_launcher_internal",
-    "//ios/chrome/browser/net",
     "//ios/chrome/browser/passwords",
     "//ios/chrome/browser/passwords:passwords_internal",
     "//ios/chrome/browser/reading_list",
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index f1119e3..db9e457 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -70,7 +70,6 @@
 #include "ios/chrome/browser/metrics/ios_chrome_origins_seen_service_factory.h"
 #import "ios/chrome/browser/metrics/tab_usage_recorder.h"
 #import "ios/chrome/browser/native_app_launcher/native_app_navigation_controller.h"
-#import "ios/chrome/browser/net/metrics_network_client_manager.h"
 #import "ios/chrome/browser/passwords/credential_manager.h"
 #import "ios/chrome/browser/passwords/js_credential_manager.h"
 #import "ios/chrome/browser/passwords/password_controller.h"
@@ -130,7 +129,6 @@
 #import "ios/web/navigation/crw_session_entry.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
-#include "ios/web/net/request_tracker_impl.h"
 #include "ios/web/public/favicon_status.h"
 #include "ios/web/public/favicon_url.h"
 #include "ios/web/public/interstitials/web_interstitial.h"
@@ -333,10 +331,6 @@
 
   // C++ observer to implement the credential management JavaScript API.
   std::unique_ptr<CredentialManager> credentialManager_;
-
-  // Client factory created for metrics tracking. The Tab will signal page
-  // load starts and finishes to this.
-  base::scoped_nsobject<MetricsNetworkClientManager> metricsClientManager_;
 }
 
 // Returns the current sessionEntry for the sesionController associated with
@@ -506,16 +500,6 @@
   [owner_ updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
 }
 
-// Registers |factory| with |tracker| on the IO thread.
-void AddNetworkClientFactoryOnIOThread(
-    web::RequestTrackerImpl* tracker,
-    CRNForwardingNetworkClientFactory* factory) {
-  base::scoped_nsobject<CRNForwardingNetworkClientFactory> scoped_factory(
-      [factory retain]);
-  tracker->PostIOTask(base::Bind(&net::RequestTracker::AddNetworkClientFactory,
-                                 tracker, scoped_factory));
-}
-
 }  // anonymous namespace
 
 @implementation Tab
@@ -649,11 +633,6 @@
         self.webState,
         ios::TopSitesFactory::GetForBrowserState(original_browser_state).get());
     [self setShouldObserveFaviconChanges:YES];
-    web::RequestTrackerImpl* requestTracker =
-        webStateImpl_->GetRequestTracker();
-
-    metricsClientManager_.reset([[MetricsNetworkClientManager alloc] init]);
-    AddNetworkClientFactoryOnIOThread(requestTracker, metricsClientManager_);
 
     if (parentModel && parentModel.syncedWindowDelegate) {
       IOSChromeSessionTabHelper::FromWebState(self.webState)
@@ -1798,7 +1777,6 @@
       postNotificationName:
           kTabClosingCurrentDocumentNotificationForCrashReporting
                     object:self];
-  [metricsClientManager_ pageLoadStarted:URL];
 }
 
 - (void)webCancelStartLoadingRequest {
@@ -1902,7 +1880,6 @@
     [self handleExportableFile:headers.get()];
   }
 
-  [metricsClientManager_ pageLoadCompleted];
   [parentTabModel_ notifyTabChanged:self];
 
   if (parentTabModel_) {
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6749fc2..31d9288 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -941,7 +941,8 @@
   _tabStripController.reset();
   _infoBarContainer.reset();
   _readingListMenuNotifier.reset();
-  _bookmarkModel->RemoveObserver(_bookmarkModelBridge.get());
+  if (_bookmarkModel)
+    _bookmarkModel->RemoveObserver(_bookmarkModelBridge.get());
   [_model removeObserver:self];
   [[UpgradeCenter sharedInstance] unregisterClient:self];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
@@ -1656,10 +1657,13 @@
   _imageFetcher->SetRequestContextGetter(_browserState->GetRequestContext());
   _dominantColorCache.reset([[NSMutableDictionary alloc] init]);
 
-  // Register for bookmark changed notification.
-  _bookmarkModelBridge.reset(new BrowserBookmarkModelBridge(self));
+  // Register for bookmark changed notification (BookmarkModel may be null
+  // during testing, so explicitly support this).
   _bookmarkModel = ios::BookmarkModelFactory::GetForBrowserState(_browserState);
-  _bookmarkModel->AddObserver(_bookmarkModelBridge.get());
+  if (_bookmarkModel) {
+    _bookmarkModelBridge.reset(new BrowserBookmarkModelBridge(self));
+    _bookmarkModel->AddObserver(_bookmarkModelBridge.get());
+  }
 }
 
 - (void)ensureViewCreated {
@@ -2155,7 +2159,7 @@
 
   // TODO(noyau): this is incorrect, the caller should know that the model is
   // not loaded yet.
-  if (!_bookmarkModel->loaded())
+  if (!_bookmarkModel || !_bookmarkModel->loaded())
     return filesReferencedByTabs;
 
   std::vector<bookmarks::BookmarkModel::URLAndTitle> bookmarks;
@@ -2482,19 +2486,19 @@
         };
         [_contextMenuCoordinator addItemWithTitle:title action:action];
       }
-
-      if (reading_list::switches::IsReadingListEnabled()) {
-        NSString* innerText = params.link_text;
-        if ([innerText length] > 0) {
-          // Add to reading list.
-          title = l10n_util::GetNSStringWithFixup(
-              IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST);
-          action = ^{
-            Record(ACTION_READ_LATER, isImage, isLink);
-            [weakSelf addToReadingListURL:link title:innerText];
-          };
-          [_contextMenuCoordinator addItemWithTitle:title action:action];
-        }
+    }
+    if (link.SchemeIsHTTPOrHTTPS() &&
+        reading_list::switches::IsReadingListEnabled()) {
+      NSString* innerText = params.link_text;
+      if ([innerText length] > 0) {
+        // Add to reading list.
+        title = l10n_util::GetNSStringWithFixup(
+            IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST);
+        action = ^{
+          Record(ACTION_READ_LATER, isImage, isLink);
+          [weakSelf addToReadingListURL:link title:innerText];
+        };
+        [_contextMenuCoordinator addItemWithTitle:title action:action];
       }
     }
     // Copy Link.
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
index c9b4648..8b20b01 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
@@ -7,8 +7,6 @@
 #import <UIKit/UIKit.h>
 
 #include "base/mac/scoped_nsobject.h"
-#include "components/bookmarks/test/bookmark_test_helpers.h"
-#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
@@ -22,14 +20,8 @@
   BrowserViewWranglerTest() {
     TestChromeBrowserState::Builder test_cbs_builder;
     chrome_browser_state_ = test_cbs_builder.Build();
-    chrome_browser_state_->CreateBookmarkModel(false);
-    bookmarks::BookmarkModel* bookmark_model =
-        ios::BookmarkModelFactory::GetForBrowserState(
-            chrome_browser_state_.get());
-    bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
   }
 
-  // SessionWindow, used to create the TabModel, needs to run on the web thread.
   web::TestWebThreadBundle thread_bundle_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
 };
diff --git a/ios/chrome/browser/ui/suggestions/BUILD.gn b/ios/chrome/browser/ui/suggestions/BUILD.gn
index 4b9e43a..d99d9c5 100644
--- a/ios/chrome/browser/ui/suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/suggestions/BUILD.gn
@@ -5,11 +5,14 @@
 source_set("suggestions") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "expandable_item.h",
     "suggestions_article_item.h",
     "suggestions_article_item.mm",
     "suggestions_collection_updater.h",
     "suggestions_collection_updater.mm",
     "suggestions_commands.h",
+    "suggestions_expandable_item.h",
+    "suggestions_expandable_item.mm",
     "suggestions_item.h",
     "suggestions_item.mm",
     "suggestions_item_actions.h",
@@ -21,6 +24,7 @@
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view",
     "//ios/third_party/material_roboto_font_loader_ios",
+    "//ui/base",
   ]
   public_deps = [
     "//ios/third_party/material_components_ios",
@@ -31,10 +35,13 @@
   testonly = true
   sources = [
     "suggestions_article_item_unittest.mm",
+    "suggestions_expandable_item_unittest.mm",
+    "suggestions_item_unittest.mm",
   ]
   deps = [
     ":suggestions",
     "//ios/chrome/browser/ui/collection_view",
     "//testing/gtest",
+    "//third_party/ocmock",
   ]
 }
diff --git a/ios/chrome/browser/ui/suggestions/expandable_item.h b/ios/chrome/browser/ui/suggestions/expandable_item.h
new file mode 100644
index 0000000..b96c112
--- /dev/null
+++ b/ios/chrome/browser/ui/suggestions/expandable_item.h
@@ -0,0 +1,18 @@
+// 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 IOS_CHROME_BROWSER_UI_SUGGESTIONS_EXPANDABLE_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_SUGGESTIONS_EXPANDABLE_ITEM_H_
+
+#import <UIKit/UIKit.h>
+
+// Protocol allowing a CollectionViewItem to be expanded.
+@protocol SuggestionsExpandableArticle
+
+// Whether the cells should be in expanded mode.
+@property(nonatomic) BOOL expanded;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SUGGESTIONS_EXPANDABLE_ITEM_H_
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h b/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h
index 48424b8..aadb227 100644
--- a/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h
+++ b/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-@class CollectionViewController;
+@class SuggestionsViewController;
 
 // Updater for a CollectionViewController populating it with some items and
 // handling the items addition.
@@ -15,7 +15,7 @@
 
 // |collectionViewController| this Updater will update.
 - (instancetype)initWithCollectionViewController:
-    (CollectionViewController*)collectionViewController
+    (SuggestionsViewController*)collectionViewController
     NS_DESIGNATED_INITIALIZER;
 - (instancetype)init NS_UNAVAILABLE;
 
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.mm b/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.mm
index 51814e35..01c6d0c 100644
--- a/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/suggestions/suggestions_collection_updater.mm
@@ -4,10 +4,13 @@
 
 #import "ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h"
 
+#include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/suggestions/suggestions_article_item.h"
+#import "ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h"
 #import "ios/chrome/browser/ui/suggestions/suggestions_item.h"
+#import "ios/chrome/browser/ui/suggestions/suggestions_view_controller.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -23,11 +26,11 @@
 }  // namespace
 
 @implementation SuggestionsCollectionUpdater {
-  CollectionViewController* _collectionViewController;
+  SuggestionsViewController* _collectionViewController;
 }
 
 - (instancetype)initWithCollectionViewController:
-    (CollectionViewController*)collectionViewController {
+    (SuggestionsViewController*)collectionViewController {
   self = [super init];
   if (self) {
     _collectionViewController = collectionViewController;
@@ -48,6 +51,19 @@
                                   @"spawn on multiple lines"
                             image:[UIImage imageNamed:@"distillation_success"]]
           toSectionWithIdentifier:sectionIdentifier];
+      SuggestionsExpandableItem* expandableItem =
+          [[SuggestionsExpandableItem alloc]
+              initWithType:ItemTypeExpand
+                     title:@"Title of an Expandable Article"
+                  subtitle:@"This Article can be expanded to display "
+                           @"additional information or interaction "
+                           @"options"
+                     image:[UIImage imageNamed:@"distillation_fail"]
+                detailText:@"Details shown only when the article is "
+                           @"expanded. It can be displayed on "
+                           @"multiple lines."];
+      expandableItem.delegate = collectionViewController;
+      [model addItem:expandableItem toSectionWithIdentifier:sectionIdentifier];
       sectionIdentifier++;
     }
   }
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h
new file mode 100644
index 0000000..174ab5c8
--- /dev/null
+++ b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h
@@ -0,0 +1,57 @@
+// 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 IOS_CHROME_BROWSER_UI_SUGGESTIONS_SUGGESTIONS_EXPANDABLE_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_SUGGESTIONS_SUGGESTIONS_EXPANDABLE_ITEM_H_
+
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
+#import "ios/chrome/browser/ui/suggestions/expandable_item.h"
+#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h"
+
+@class SuggestionsExpandableCell;
+
+// Delegate for the SuggestionsExpandableCell. The delegate will take care of
+// the expansion/collapsing of the cell.
+@protocol SuggestionsExpandableCellDelegate
+
+- (void)expandCell:(UICollectionViewCell*)cell;
+- (void)collapseCell:(UICollectionViewCell*)cell;
+
+@end
+
+// Item for an expandable article in the suggestions. An expandable article can
+// be expanded, displaying more informations/interactions.
+@interface SuggestionsExpandableItem
+    : CollectionViewItem<SuggestionsExpandableArticle>
+
+// Init the article with a |title|, a |subtitle| an |image| and some |detail|
+// displayed only when the article is expanded. |type| is the type of the item.
+- (instancetype)initWithType:(NSInteger)type
+                       title:(NSString*)title
+                    subtitle:(NSString*)subtitle
+                       image:(UIImage*)image
+                  detailText:(NSString*)detail NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithType:(NSInteger)type NS_UNAVAILABLE;
+
+// Delegate for the configured cells.
+@property(nonatomic, weak) id<SuggestionsExpandableCellDelegate> delegate;
+
+@end
+
+// Corresponding cell of an expandable article.
+@interface SuggestionsExpandableCell : MDCCollectionViewCell
+
+@property(nonatomic, weak) id<SuggestionsExpandableCellDelegate> delegate;
+@property(nonatomic, readonly, strong) UILabel* titleLabel;
+@property(nonatomic, readonly, strong) UILabel* subtitleLabel;
+@property(nonatomic, readonly, strong) UILabel* detailLabel;
+@property(nonatomic, strong) UIImageView* imageView;
+
+- (void)expand;
+- (void)collapse;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SUGGESTIONS_SUGGESTIONS_EXPANDABLE_ITEM_H_
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.mm b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.mm
new file mode 100644
index 0000000..761294e
--- /dev/null
+++ b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item.mm
@@ -0,0 +1,237 @@
+// 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 "ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h"
+
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const CGFloat kImageSize = 80;
+const CGFloat kStandardSpacing = 8;
+}
+
+#pragma mark - SuggestionsExpandableItem
+
+@implementation SuggestionsExpandableItem {
+  NSString* _title;
+  NSString* _subtitle;
+  UIImage* _image;
+  NSString* _detail;
+}
+
+@synthesize delegate = _delegate;
+@synthesize expanded = _expanded;
+
+- (instancetype)initWithType:(NSInteger)type
+                       title:(NSString*)title
+                    subtitle:(NSString*)subtitle
+                       image:(UIImage*)image
+                  detailText:(NSString*)detail {
+  self = [super initWithType:type];
+  if (self) {
+    self.cellClass = [SuggestionsExpandableCell class];
+    _title = [title copy];
+    _subtitle = [subtitle copy];
+    _image = image;
+    _detail = [detail copy];
+  }
+  return self;
+}
+
+#pragma mark - CollectionViewItem
+
+- (void)configureCell:(SuggestionsExpandableCell*)cell {
+  [super configureCell:cell];
+  cell.delegate = self.delegate;
+  cell.titleLabel.text = _title;
+  cell.subtitleLabel.text = _subtitle;
+  cell.imageView.image = _image;
+  cell.detailLabel.text = _detail;
+  if (self.expanded)
+    [cell expand];
+  else
+    [cell collapse];
+}
+
+@end
+
+#pragma mark - SuggestionsExpandableCell
+
+@interface SuggestionsExpandableCell () {
+  UIView* _articleContainer;
+  UIButton* _interactionButton;
+  UIButton* _expandButton;
+  BOOL _expanded;
+  NSArray<NSLayoutConstraint*>* _expandedConstraints;
+  NSArray<NSLayoutConstraint*>* _collapsedConstraints;
+}
+
+// Callback for the UI action showing the details.
+- (void)expand:(id)sender;
+// Callback for the UI action hiding the details.
+- (void)collapse:(id)sender;
+
+@end
+
+@implementation SuggestionsExpandableCell
+
+@synthesize titleLabel = _titleLabel;
+@synthesize subtitleLabel = _subtitleLabel;
+@synthesize detailLabel = _detailLabel;
+@synthesize imageView = _imageView;
+@synthesize delegate = _delegate;
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    _expanded = NO;
+    _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _subtitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
+    _articleContainer = [[UIView alloc] initWithFrame:CGRectZero];
+    _expandButton = [UIButton buttonWithType:UIButtonTypeSystem];
+    _detailLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _interactionButton = [UIButton buttonWithType:UIButtonTypeSystem];
+
+    _subtitleLabel.numberOfLines = 0;
+    [_expandButton setTitle:@"See more" forState:UIControlStateNormal];
+    _detailLabel.numberOfLines = 0;
+    [_interactionButton setTitle:@"Less interaction"
+                        forState:UIControlStateNormal];
+
+    _imageView.translatesAutoresizingMaskIntoConstraints = NO;
+    _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    _subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    _articleContainer.translatesAutoresizingMaskIntoConstraints = NO;
+    _expandButton.translatesAutoresizingMaskIntoConstraints = NO;
+    _detailLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    _interactionButton.translatesAutoresizingMaskIntoConstraints = NO;
+
+    [_expandButton addTarget:self
+                      action:@selector(expand:)
+            forControlEvents:UIControlEventTouchUpInside];
+    [_interactionButton addTarget:self
+                           action:@selector(collapse:)
+                 forControlEvents:UIControlEventTouchUpInside];
+
+    [_articleContainer addSubview:_imageView];
+    [_articleContainer addSubview:_titleLabel];
+    [_articleContainer addSubview:_subtitleLabel];
+
+    [self.contentView addSubview:_articleContainer];
+    [self.contentView addSubview:_expandButton];
+
+    [NSLayoutConstraint activateConstraints:@[
+      [self.contentView.centerXAnchor
+          constraintEqualToAnchor:_expandButton.centerXAnchor],
+      [_expandButton.topAnchor
+          constraintEqualToAnchor:_articleContainer.bottomAnchor],
+      [_expandButton.bottomAnchor
+          constraintEqualToAnchor:self.contentView.bottomAnchor],
+      [_articleContainer.bottomAnchor
+          constraintGreaterThanOrEqualToAnchor:_imageView.bottomAnchor
+                                      constant:kStandardSpacing],
+      [_articleContainer.bottomAnchor
+          constraintGreaterThanOrEqualToAnchor:_subtitleLabel.bottomAnchor
+                                      constant:kStandardSpacing],
+    ]];
+
+    ApplyVisualConstraintsWithMetrics(
+        @[
+          @"H:|[container]|", @"H:|-[title]-[image(imageSize)]-|",
+          @"H:|-[text]-[image]", @"V:|-[image(imageSize)]",
+          @"V:|-[title]-[text]", @"V:|[container]"
+        ],
+        @{
+          @"image" : _imageView,
+          @"title" : _titleLabel,
+          @"text" : _subtitleLabel,
+          @"container" : _articleContainer
+        },
+        @{ @"imageSize" : @(kImageSize) });
+
+    _expandedConstraints = @[
+      [_detailLabel.topAnchor
+          constraintEqualToAnchor:_articleContainer.bottomAnchor],
+      [_detailLabel.bottomAnchor
+          constraintEqualToAnchor:_interactionButton.topAnchor],
+      [_interactionButton.bottomAnchor
+          constraintEqualToAnchor:self.contentView.bottomAnchor],
+      [_detailLabel.leadingAnchor
+          constraintEqualToAnchor:self.contentView.leadingAnchor],
+      [_detailLabel.trailingAnchor
+          constraintEqualToAnchor:self.contentView.trailingAnchor]
+    ];
+    _collapsedConstraints = @[
+      [self.contentView.centerXAnchor
+          constraintEqualToAnchor:_expandButton.centerXAnchor],
+      [_expandButton.topAnchor
+          constraintEqualToAnchor:_articleContainer.bottomAnchor],
+      [_expandButton.bottomAnchor
+          constraintEqualToAnchor:self.contentView.bottomAnchor]
+    ];
+  }
+  return self;
+}
+
+#pragma mark - Private
+
+- (void)expand:(id)sender {
+  [self.delegate expandCell:self];
+}
+
+- (void)collapse:(id)sender {
+  [self.delegate collapseCell:self];
+}
+
+- (void)expand {
+  if (_expanded)
+    return;
+  _expanded = YES;
+
+  [self.contentView addSubview:_detailLabel];
+  [self.contentView addSubview:_interactionButton];
+  [_expandButton removeFromSuperview];
+
+  [NSLayoutConstraint deactivateConstraints:_collapsedConstraints];
+  [NSLayoutConstraint activateConstraints:_expandedConstraints];
+}
+
+- (void)collapse {
+  if (!_expanded)
+    return;
+  _expanded = NO;
+
+  [_detailLabel removeFromSuperview];
+  [_interactionButton removeFromSuperview];
+  [self.contentView addSubview:_expandButton];
+
+  [NSLayoutConstraint deactivateConstraints:_expandedConstraints];
+  [NSLayoutConstraint activateConstraints:_collapsedConstraints];
+}
+
+#pragma mark - UIView
+
+// Implements -layoutSubviews as per instructions in documentation for
+// +[MDCCollectionViewCell cr_preferredHeightForWidth:forItem:].
+- (void)layoutSubviews {
+  [super layoutSubviews];
+
+  // Adjust the text label preferredMaxLayoutWidth when the parent's width
+  // changes, for instance on screen rotation.
+  CGFloat parentWidth = CGRectGetWidth(self.contentView.bounds);
+  _subtitleLabel.preferredMaxLayoutWidth = parentWidth - kImageSize - 3 * 8;
+  _detailLabel.preferredMaxLayoutWidth = parentWidth;
+
+  // Re-layout with the new preferred width to allow the label to adjust its
+  // height.
+  [super layoutSubviews];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_expandable_item_unittest.mm b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item_unittest.mm
new file mode 100644
index 0000000..906abf7
--- /dev/null
+++ b/ios/chrome/browser/ui/suggestions/suggestions_expandable_item_unittest.mm
@@ -0,0 +1,104 @@
+// 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 "ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h"
+
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+// Test subclass of the SuggestionsExpandableCell.
+@interface TestSuggestionsExpandableCell : SuggestionsExpandableCell
+
+@property(nonatomic) BOOL expandCalled;
+@property(nonatomic) BOOL collapseCalled;
+
+@end
+
+@implementation TestSuggestionsExpandableCell
+
+@synthesize expandCalled;
+@synthesize collapseCalled;
+
+- (void)expand {
+  [super expand];
+  self.expandCalled = YES;
+}
+
+- (void)collapse {
+  [super collapse];
+  self.collapseCalled = YES;
+}
+
+@end
+
+namespace {
+
+// Tests that configureCell: set all the fields of the cell.
+TEST(SuggestionsExpandableItemTest, CellIsConfigured) {
+  NSString* title = @"testTitle";
+  NSString* subtitle = @"testSubtitle";
+  UIImage* image = [[UIImage alloc] init];
+  NSString* details = @"testDetails";
+  id mockDelegate = [OCMockObject
+      mockForProtocol:@protocol(SuggestionsExpandableCellDelegate)];
+
+  SuggestionsExpandableItem* item =
+      [[SuggestionsExpandableItem alloc] initWithType:0
+                                                title:title
+                                             subtitle:subtitle
+                                                image:image
+                                           detailText:details];
+  item.delegate = mockDelegate;
+  SuggestionsExpandableCell* cell = [[[item cellClass] alloc] init];
+  EXPECT_TRUE([cell isMemberOfClass:[SuggestionsExpandableCell class]]);
+
+  [item configureCell:cell];
+  EXPECT_EQ(title, cell.titleLabel.text);
+  EXPECT_EQ(subtitle, cell.subtitleLabel.text);
+  EXPECT_EQ(image, cell.imageView.image);
+  EXPECT_EQ(details, cell.detailLabel.text);
+  EXPECT_EQ(mockDelegate, cell.delegate);
+}
+
+// Tests that if the expanded property of the item is YES, the |expand| method
+// of the cell is called during configuration.
+TEST(SuggestionsExpandableItemTest, CellIsExpanded) {
+  SuggestionsExpandableItem* item =
+      [[SuggestionsExpandableItem alloc] initWithType:0
+                                                title:@"title"
+                                             subtitle:@"subtitle"
+                                                image:nil
+                                           detailText:@"detail"];
+  TestSuggestionsExpandableCell* cell =
+      [[TestSuggestionsExpandableCell alloc] init];
+  item.cellClass = [TestSuggestionsExpandableCell class];
+
+  item.expanded = YES;
+  [item configureCell:cell];
+  ASSERT_TRUE(cell.expandCalled);
+  ASSERT_FALSE(cell.collapseCalled);
+}
+
+// Tests that if the expanded property of the item is NO, the |collapse| method
+// of the cell is called during configuration.
+TEST(SuggestionsExpandableItemTest, CellIsCollapsed) {
+  SuggestionsExpandableItem* item =
+      [[SuggestionsExpandableItem alloc] initWithType:0
+                                                title:@"title"
+                                             subtitle:@"subtitle"
+                                                image:nil
+                                           detailText:@"detail"];
+  TestSuggestionsExpandableCell* cell =
+      [[TestSuggestionsExpandableCell alloc] init];
+  item.cellClass = [TestSuggestionsExpandableCell class];
+
+  item.expanded = NO;
+  [item configureCell:cell];
+  ASSERT_TRUE(cell.collapseCalled);
+  ASSERT_FALSE(cell.expandCalled);
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_item_unittest.mm b/ios/chrome/browser/ui/suggestions/suggestions_item_unittest.mm
new file mode 100644
index 0000000..d5ae0f85
--- /dev/null
+++ b/ios/chrome/browser/ui/suggestions/suggestions_item_unittest.mm
@@ -0,0 +1,26 @@
+// 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 "ios/chrome/browser/ui/suggestions/suggestions_item.h"
+
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Tests that configureCell: set all the fields of the cell.
+TEST(SuggestionsItemTest, CellIsConfigured) {
+  NSString* title = @"testTitle";
+  NSString* subtitle = @"testSubtitle";
+  SuggestionsItem* item =
+      [[SuggestionsItem alloc] initWithType:0 title:title subtitle:subtitle];
+  SuggestionsCell* cell = [[[item cellClass] alloc] init];
+  EXPECT_TRUE([cell isMemberOfClass:[SuggestionsCell class]]);
+
+  [item configureCell:cell];
+  EXPECT_EQ(title, [cell.titleButton titleForState:UIControlStateNormal]);
+  EXPECT_EQ(subtitle, cell.detailTextLabel.text);
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_view_controller.h b/ios/chrome/browser/ui/suggestions/suggestions_view_controller.h
index b058da8..8d81fd01 100644
--- a/ios/chrome/browser/ui/suggestions/suggestions_view_controller.h
+++ b/ios/chrome/browser/ui/suggestions/suggestions_view_controller.h
@@ -8,11 +8,13 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
+#import "ios/chrome/browser/ui/suggestions/suggestions_expandable_item.h"
 
 @protocol SuggestionsCommands;
 
 // CollectionViewController to display the suggestions items.
-@interface SuggestionsViewController : CollectionViewController
+@interface SuggestionsViewController
+    : CollectionViewController<SuggestionsExpandableCellDelegate>
 
 // Handler for the commands sent by the SuggestionsViewController.
 @property(nonatomic, weak) id<SuggestionsCommands> suggestionCommandHandler;
diff --git a/ios/chrome/browser/ui/suggestions/suggestions_view_controller.mm b/ios/chrome/browser/ui/suggestions/suggestions_view_controller.mm
index a27da97a..84a8ef63 100644
--- a/ios/chrome/browser/ui/suggestions/suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/suggestions/suggestions_view_controller.mm
@@ -4,9 +4,11 @@
 
 #import "ios/chrome/browser/ui/suggestions/suggestions_view_controller.h"
 
+#include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
+#import "ios/chrome/browser/ui/suggestions/expandable_item.h"
 #import "ios/chrome/browser/ui/suggestions/suggestions_collection_updater.h"
 #import "ios/chrome/browser/ui/suggestions/suggestions_commands.h"
 #import "ios/chrome/browser/ui/suggestions/suggestions_item_actions.h"
@@ -15,10 +17,18 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+const NSTimeInterval kAnimationDuration = 0.35;
+}  // namespace
+
 @interface SuggestionsViewController ()<SuggestionsItemActions>
 
 @property(nonatomic, strong) SuggestionsCollectionUpdater* collectionUpdater;
 
+// Expand or collapse the |cell|, if it is a SuggestionsExpandableCell,
+// according to |expand|.
+- (void)expand:(BOOL)expand cell:(UICollectionViewCell*)cell;
+
 @end
 
 @implementation SuggestionsViewController
@@ -54,6 +64,16 @@
                          forItem:item];
 }
 
+#pragma mark - SuggestionsExpandableCellDelegate
+
+- (void)collapseCell:(UICollectionViewCell*)cell {
+  [self expand:NO cell:cell];
+}
+
+- (void)expandCell:(UICollectionViewCell*)cell {
+  [self expand:YES cell:cell];
+}
+
 #pragma mark - SuggestionsItemActions
 
 - (void)addNewItem:(id)sender {
@@ -70,4 +90,29 @@
                             toSection:inputSection];
 }
 
+#pragma mark - Private
+
+- (void)expand:(BOOL)expand cell:(UICollectionViewCell*)cell {
+  NSIndexPath* indexPath = [self.collectionView indexPathForCell:cell];
+  CollectionViewItem* item =
+      [self.collectionViewModel itemAtIndexPath:indexPath];
+  if ([item conformsToProtocol:@protocol(SuggestionsExpandableArticle)]) {
+    id<SuggestionsExpandableArticle> expandableItem =
+        (id<SuggestionsExpandableArticle>)item;
+
+    NSInteger sectionIdentifier = [self.collectionViewModel
+        sectionIdentifierForSection:indexPath.section];
+
+    expandableItem.expanded = expand;
+    [self reconfigureCellsForItems:@[ item ]
+           inSectionWithIdentifier:sectionIdentifier];
+
+    [UIView
+        animateWithDuration:kAnimationDuration
+                 animations:^{
+                   [self.collectionView.collectionViewLayout invalidateLayout];
+                 }];
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
index ecf5386..c36e931e5 100644
--- a/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
+++ b/ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -34,6 +34,7 @@
   void RegisterMessages() override;
 
   // ntp_tiles::NTPTilesInternalsMessageHandlerClient
+  bool SupportsNTPTiles() override;
   bool DoesSourceExist(ntp_tiles::NTPTileSource source) override;
   std::unique_ptr<ntp_tiles::MostVisitedSites> MakeMostVisitedSites() override;
   std::unique_ptr<ntp_tiles::PopularSites> MakePopularSites() override;
@@ -54,6 +55,11 @@
   handler_.RegisterMessages(this);
 }
 
+bool IOSNTPTilesInternalsMessageHandlerBridge::SupportsNTPTiles() {
+  auto* state = ios::ChromeBrowserState::FromWebUIIOS(web_ui());
+  return !state->HasOffTheRecordChromeBrowserState();
+}
+
 bool IOSNTPTilesInternalsMessageHandlerBridge::DoesSourceExist(
     ntp_tiles::NTPTileSource source) {
   switch (source) {
diff --git a/ios/chrome/browser/web/browsing_egtest.mm b/ios/chrome/browser/web/browsing_egtest.mm
index e136731c..f23a1faa 100644
--- a/ios/chrome/browser/web/browsing_egtest.mm
+++ b/ios/chrome/browser/web/browsing_egtest.mm
@@ -580,7 +580,15 @@
 // Tests that submitting a POST-based form by tapping the 'Go' button on the
 // keyboard navigates to the correct URL and the back button works as expected
 // afterwards.
-- (void)testBrowsingPostEntryWithKeyboard {
+// TODO(crbug.com/670380): Enable this test on device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testBrowsingPostEntryWithKeyboard \
+  testBrowsingPostEntryWithKeyboard
+#else
+#define MAYBE_testBrowsingPostEntryWithKeyboard \
+  FLAKY_testBrowsingPostEntryWithKeyboard
+#endif
+- (void)MAYBE_testBrowsingPostEntryWithKeyboard {
   // Create map of canned responses and set up the test HTML server.
   std::map<GURL, std::string> responses;
   const GURL URL =
diff --git a/ios/chrome/common/app_group/BUILD.gn b/ios/chrome/common/app_group/BUILD.gn
index 10c0883..2f482d83 100644
--- a/ios/chrome/common/app_group/BUILD.gn
+++ b/ios/chrome/common/app_group/BUILD.gn
@@ -36,6 +36,7 @@
 }
 
 source_set("main_app") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "app_group_metrics_mainapp.h",
     "app_group_metrics_mainapp.mm",
diff --git a/ios/chrome/common/app_group/app_group_metrics_mainapp.h b/ios/chrome/common/app_group/app_group_metrics_mainapp.h
index af693f27..5115bf03 100644
--- a/ios/chrome/common/app_group/app_group_metrics_mainapp.h
+++ b/ios/chrome/common/app_group/app_group_metrics_mainapp.h
@@ -8,7 +8,6 @@
 #import <Foundation/Foundation.h>
 #include <stdint.h>
 
-#include "base/mac/scoped_block.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
 
 namespace app_group {
@@ -20,8 +19,7 @@
 
 // Iterates through the extensions pending logs and deletes them.
 // Calls |callback| on each log before deleting.
-void ProcessPendingLogs(
-    const base::mac::ScopedBlock<ProceduralBlockWithData>& callback);
+void ProcessPendingLogs(ProceduralBlockWithData callback);
 
 // Enables the metrics collecting in extensions. The extensions will
 // use |clientID| as client ID, and |brandCode| as brand code in the logs.
diff --git a/ios/chrome/common/app_group/app_group_metrics_mainapp.mm b/ios/chrome/common/app_group/app_group_metrics_mainapp.mm
index 3e32438..b33cfec0 100644
--- a/ios/chrome/common/app_group/app_group_metrics_mainapp.mm
+++ b/ios/chrome/common/app_group/app_group_metrics_mainapp.mm
@@ -7,16 +7,18 @@
 #include <stdint.h>
 
 #include "base/logging.h"
-#include "base/mac/scoped_nsobject.h"
 #include "ios/chrome/common/app_group/app_group_constants.h"
 #include "ios/chrome/common/app_group/app_group_metrics.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace app_group {
 
 namespace main_app {
 
-void ProcessPendingLogs(
-    const base::mac::ScopedBlock<ProceduralBlockWithData>& callback) {
+void ProcessPendingLogs(ProceduralBlockWithData callback) {
   NSFileManager* file_manager = [NSFileManager defaultManager];
   NSURL* store_url = [file_manager
       containerURLForSecurityApplicationGroupIdentifier:ApplicationGroup()];
@@ -34,7 +36,7 @@
           [log_dir_url URLByAppendingPathComponent:pending_log isDirectory:NO];
       if (callback) {
         NSData* log_content = [file_manager contentsAtPath:[file_url path]];
-        callback.get()(log_content);
+        callback(log_content);
       }
       [file_manager removeItemAtURL:file_url error:nil];
     }
@@ -45,8 +47,8 @@
                    NSString* brand_code,
                    int64_t install_date,
                    int64_t enable_metrics_date) {
-  base::scoped_nsobject<NSUserDefaults> shared_defaults(
-      [[NSUserDefaults alloc] initWithSuiteName:ApplicationGroup()]);
+  NSUserDefaults* shared_defaults =
+      [[NSUserDefaults alloc] initWithSuiteName:ApplicationGroup()];
   [shared_defaults setObject:client_id forKey:@(kChromeAppClientID)];
 
   [shared_defaults
@@ -60,8 +62,8 @@
 }
 
 void DisableMetrics() {
-  base::scoped_nsobject<NSUserDefaults> shared_defaults(
-      [[NSUserDefaults alloc] initWithSuiteName:ApplicationGroup()]);
+  NSUserDefaults* shared_defaults =
+      [[NSUserDefaults alloc] initWithSuiteName:ApplicationGroup()];
   [shared_defaults removeObjectForKey:@(kChromeAppClientID)];
 }
 
diff --git a/ios/net/crn_http_protocol_handler.mm b/ios/net/crn_http_protocol_handler.mm
index bb9ac3b..c067305 100644
--- a/ios/net/crn_http_protocol_handler.mm
+++ b/ios/net/crn_http_protocol_handler.mm
@@ -663,11 +663,6 @@
   if (tracker_) {
     // Set up any clients that can operate regardless of the request
     PushClients(tracker_->ClientsHandlingAnyRequest());
-  } else {
-    // There was no request_group_id, so the request was from something like a
-    // data: or file: URL.
-    // Attach any global clients to the request.
-    PushClients(RequestTracker::GlobalClientsHandlingAnyRequest());
   }
 
   // Now that all of the network clients are set up, if there was an error with
diff --git a/ios/net/request_tracker.h b/ios/net/request_tracker.h
index 50fcc517..117b412 100644
--- a/ios/net/request_tracker.h
+++ b/ios/net/request_tracker.h
@@ -78,14 +78,6 @@
   // this tracker.
   void AddNetworkClientFactory(CRNForwardingNetworkClientFactory* factory);
 
-  // Registers a factory with the class that will be added to all trackers.
-  // Requests without associated trackers can add clients from these factories
-  // using GlobalClientsHandlingAnyRequest().
-  // Only |-clientHandlingAnyRequest| will be called on |factory|, the other
-  // methods are not supported.
-  static void AddGlobalNetworkClientFactory(
-      CRNForwardingNetworkClientFactory* factory);
-
   // Gets the request context associated with the tracker.
   virtual URLRequestContext* GetRequestContext() = 0;
 
@@ -95,10 +87,6 @@
   // may be empty. The caller is responsible for taking ownership of the clients
   // in the array.
 
-  // Static method that returns clients that can handle any request, for use
-  // in cases where a request isn't associated with any request_tracker.
-  static NSArray* GlobalClientsHandlingAnyRequest();
-
   // Returns clients that can handle any request.
   NSArray* ClientsHandlingAnyRequest();
   // Returns clients that can handle |request|.
diff --git a/ios/net/request_tracker.mm b/ios/net/request_tracker.mm
index f72921a3..85dce7f 100644
--- a/ios/net/request_tracker.mm
+++ b/ios/net/request_tracker.mm
@@ -19,56 +19,6 @@
 // Reference to the single instance of the RequestTrackerFactory.
 RequestTracker::RequestTrackerFactory* g_request_tracker_factory = nullptr;
 
-// Array of network client factories that should be added to any new request
-// tracker.
-class GlobalNetworkClientFactories {
- public:
-  static GlobalNetworkClientFactories* GetInstance() {
-    if (!g_global_network_client_factories)
-      g_global_network_client_factories = new GlobalNetworkClientFactories;
-    return g_global_network_client_factories;
-  }
-
-  // Gets the factories.
-  NSArray* GetFactories() {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    return factories_.get();
-  }
-
-  // Adds a factory.
-  void AddFactory(CRNForwardingNetworkClientFactory* factory) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    DCHECK_EQ(NSNotFound,
-              static_cast<NSInteger>([factories_ indexOfObject:factory]));
-    DCHECK(!IsSelectorOverriden(factory, @selector(clientHandlingRequest:)));
-    DCHECK(!IsSelectorOverriden(factory,
-                                @selector(clientHandlingResponse:request:)));
-    DCHECK(!IsSelectorOverriden(
-               factory, @selector(clientHandlingRedirect:url:response:)));
-    [factories_ addObject:factory];
-  }
-
-  // Returns true if |factory| re-implements |selector|.
-  // Only used for debugging.
-  bool IsSelectorOverriden(CRNForwardingNetworkClientFactory* factory,
-                           SEL selector) {
-    return
-        [factory methodForSelector:selector] !=
-        [CRNForwardingNetworkClientFactory instanceMethodForSelector:selector];
-  }
-
- private:
-  GlobalNetworkClientFactories() : factories_([[NSMutableArray alloc] init]) {}
-
-  base::scoped_nsobject<NSMutableArray> factories_;
-  base::ThreadChecker thread_checker_;
-
-  static GlobalNetworkClientFactories* g_global_network_client_factories;
-};
-
-GlobalNetworkClientFactories*
-    GlobalNetworkClientFactories::g_global_network_client_factories = nullptr;
-
 }  // namespace
 
 RequestTracker::RequestTrackerFactory::~RequestTrackerFactory() {
@@ -108,12 +58,6 @@
   }
 }
 
-// static
-void RequestTracker::AddGlobalNetworkClientFactory(
-    CRNForwardingNetworkClientFactory* factory) {
-  GlobalNetworkClientFactories::GetInstance()->AddFactory(factory);
-}
-
 RequestTracker::RequestTracker()
     : client_factories_([[NSMutableArray alloc] init]),
       initialized_(false),
@@ -139,22 +83,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!initialized_);
   initialized_ = true;
-  for (CRNForwardingNetworkClientFactory* factory in
-           GlobalNetworkClientFactories::GetInstance()->GetFactories()) {
-    AddNetworkClientFactory(factory);
-  }
-}
-
-// static
-NSArray* RequestTracker::GlobalClientsHandlingAnyRequest() {
-  NSMutableArray* applicable_clients = [NSMutableArray array];
-  for (CRNForwardingNetworkClientFactory* factory in
-           GlobalNetworkClientFactories::GetInstance()->GetFactories()) {
-    CRNForwardingNetworkClient* client = [factory clientHandlingAnyRequest];
-    if (client)
-      [applicable_clients addObject:client];
-  }
-  return applicable_clients;
 }
 
 NSArray* RequestTracker::ClientsHandlingAnyRequest() {
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index 7547b6b7..38aef4f0 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -96,17 +96,11 @@
 
   // Initializes the cast service.  Should be called soon after the given
   // |main_tab_model| is created.
-  // TODO(rohitrao): Change from |id| to |TabModel*| once TabModel is moved into
-  // the Chromium tree.
-  virtual void InitializeCastService(id main_tab_model) const;
   virtual void InitializeCastService(TabModel* main_tab_model) const;
 
   // Attaches any embedder-specific tab helpers to the given |web_state|.  The
   // owning |tab| is included for helpers that need access to information that
   // is not yet available through web::WebState.
-  // TODO(rohitrao): Change from |id| to |Tab*| once Tab is moved into the
-  // Chromium tree.
-  virtual void AttachTabHelpers(web::WebState* web_state, id tab) const;
   virtual void AttachTabHelpers(web::WebState* web_state, Tab* tab) const;
 
   // Returns whether safe browsing is enabled. See the comment on
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index 9e1d496..1c9a88d 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -74,24 +74,11 @@
   return nil;
 }
 
-void ChromeBrowserProvider::InitializeCastService(id main_tab_model) const {}
-
 void ChromeBrowserProvider::InitializeCastService(
-    TabModel* main_tab_model) const {
-  // Invokes the version taking an "id" as this is the only version currently
-  // overridden by the downstream provider.
-  InitializeCastService(static_cast<id>(main_tab_model));
-}
+    TabModel* main_tab_model) const {}
 
 void ChromeBrowserProvider::AttachTabHelpers(web::WebState* web_state,
-                                             id tab) const {}
-
-void ChromeBrowserProvider::AttachTabHelpers(web::WebState* web_state,
-                                             Tab* tab) const {
-  // Invokes the version taking an "id" as this is the only version currently
-  // overridden by the downstream provider.
-  AttachTabHelpers(web_state, static_cast<id>(tab));
-}
+                                             Tab* tab) const {}
 
 bool ChromeBrowserProvider::IsSafeBrowsingEnabled(
     const base::Closure& on_update_callback) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 30a468b..45b5967dc 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -829,25 +829,6 @@
             ]
         }
     ],
-    "HttpFormWarning": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "HttpFormWarning"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSurfaceClearYosemite": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 2a1c0fe..c70b349 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -98,6 +98,8 @@
 crbug.com/506754 virtual/mojo-loading/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
 crbug.com/506754 virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/resolve-after-window-close.html [ Leak ]
 crbug.com/506754 virtual/service-worker-navigation-preload/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
+crbug.com/506754 virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/resolve-after-window-close.html [ Leak ]
+crbug.com/506754 virtual/service-worker-navigation-preload-disabled/http/tests/serviceworker/chromium/window-close-during-registration.html [ Leak ]
 
 # -----------------------------------------------------------------
 # Untriaged but known leaks of ActiveDOMObject (Web Audio).
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 2499915..5f431ab 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -102,7 +102,6 @@
 crbug.com/632000 [ Win ] images/paint-subrect-grid.html [ Pass Failure ]
 
 crbug.com/636271 [ Mac10.10 ] paint/invalidation/resize-iframe-text.html [ Pass Failure ]
-crbug.com/636271 [ Mac10.10 ] virtual/stable/paint/invalidation/resize-iframe-text.html [ Pass Failure ]
 
 crbug.com/639147 svg/wicd/test-rightsizing-b.xhtml [ Failure Pass ]
 
@@ -2322,8 +2321,8 @@
 crbug.com/678499 http/tests/security/contentSecurityPolicy/require-sri-for/require-sri-for-script-preload-allowed.php [ Failure Pass ]
 crbug.com/678499 virtual/mojo-loading/http/tests/security/contentSecurityPolicy/require-sri-for/require-sri-for-script-preload-allowed.php [ Failure Pass ]
 
-crbug.com/681391 virtual/gpu-rasterization/images/imagemap-circle-focus-ring.html [ NeedsManualRebaseline ]
-crbug.com/681391 virtual/gpu-rasterization/images/imagemap-overflowing-circle-focus-ring.html [ NeedsManualRebaseline ]
+crbug.com/681391 virtual/gpu-rasterization/images/imagemap-circle-focus-ring.html [ NeedsRebaseline ]
+crbug.com/681391 virtual/gpu-rasterization/images/imagemap-overflowing-circle-focus-ring.html [ NeedsRebaseline ]
 
 crbug.com/680043 sensor/ambient-light-sensor.html [ Pass Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js
index 7d5ad6d..100bcba 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/resources/helper.js
@@ -4,6 +4,16 @@
   }));
 }
 
+function appendFrameAndGetElement(test, frame) {
+  return new Promise((resolve, reject) => {
+    frame.onload = test.step_func(_ => {
+      frame.onload = null;
+      resolve(frame.contentDocument.querySelector('#dangling'));
+    });
+    document.body.appendChild(frame);
+  });
+}
+
 function appendAndSubmit(test, frame) {
   return new Promise((resolve, reject) => {
     frame.onload = test.step_func(_ => {
@@ -30,6 +40,22 @@
     }));
 }
 
+function assert_img_loaded(test, frame) {
+  appendFrameAndGetElement(test, frame)
+    .then(test.step_func_done(img => {
+      assert_equals(img.naturalHeight, 103, "Height");
+      assert_equals(img.naturalWidth, 76, "Width");
+    }));
+}
+
+function assert_img_not_loaded(test, frame) {
+  appendFrameAndGetElement(test, frame)
+    .then(test.step_func_done(img => {
+      assert_equals(img.naturalHeight, 0, "Height");
+      assert_equals(img.naturalWidth, 0, "Width");
+    }));
+}
+
 function createFrame(markup) {
   var i = document.createElement('iframe');
   i.srcdoc = `${markup}sekrit`;
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html
new file mode 100644
index 0000000..7c3639e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/helper.js"></script>
+<body>
+<script>
+  // We're injecting markup via `srcdoc` so, confusingly, we need to
+  // entity-escape the "raw" content, and double-escape the "escaped"
+  // content.
+  var rawBrace = "&lt;";
+  var escapedBrace = "&amp;lt;";
+  var rawNewline = "&#10;";
+  var escapedNewline = "&amp;#10;";
+
+  var abeSizedPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEwAAABnAQMAAACQMjadAAAAA1BMVEX///+nxBvIAAAAEUlEQVQ4y2MYBaNgFIwCegAABG0AAd5G4RkAAAAASUVORK5CYII=";
+  var abeSizedPngWithNewline = abeSizedPng.replace("i", "i\n");
+
+  var should_block = [
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${rawNewline}b${rawBrace}c">`,
+    `
+      <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a
+        b${rawBrace}c
+      ">
+    `,
+  ];
+
+  should_block.forEach(markup => {
+    async_test(t => {
+      var i = createFrame(`${markup}`);
+      assert_img_not_loaded(t, i);
+    }, markup.replace(/[\n\r]/g, ''));
+  });
+
+  var should_load = [
+
+    // `data:` and `javascript:` URLs don't check the content:
+    `<img id="dangling" src="${abeSizedPngWithNewline}">`,
+
+    // Just one or the other isn't enough:
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=a${rawNewline}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${rawBrace}b">`,
+
+    // Entity-escaped characters don't trigger blocking:
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=a${escapedNewline}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${escapedBrace}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${escapedNewline}b${escapedBrace}c">`,
+
+    // Leading and trailing whitespace is stripped:
+    `
+      <img id="dangling" src="
+        http://127.0.0.1:8000/security/resources/abe.png
+      ">
+      <input type=hidden name=csrf value=sekrit>
+    `,
+    `
+      <img id="dangling" src="
+      http://127.0.0.1:8000/security/resources/abe.png?img=${escapedBrace}
+      ">
+      <input type=hidden name=csrf value=sekrit>
+    `,
+    `
+      <img id="dangling" src="
+      http://127.0.0.1:8000/security/resources/abe.png?img=${escapedNewline}
+      ">
+      <input type=hidden name=csrf value=sekrit>
+    `,
+  ];
+
+  should_load.forEach(markup => {
+    async_test(t => {
+      var i = createFrame(`${markup} <element attr="" another=''>`);
+      assert_img_loaded(t, i);
+    }, markup.replace(/[\n\r]/g, ''));
+  });
+</script>
+  
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.png
new file mode 100644
index 0000000..3ff17ff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
index f2b69df4..f91329c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
@@ -2,23 +2,33 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [800, 681],
+      "bounds": [800, 745],
       "contentsOpaque": true,
       "drawsContent": true,
       "paintInvalidations": [
         {
           "object": "LayoutIFrame (positioned) IFRAME",
-          "rect": [0, 0, 800, 681],
+          "rect": [0, 0, 800, 745],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutView #document",
-          "rect": [0, 600, 800, 81],
+          "rect": [0, 600, 800, 145],
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 745],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
-          "rect": [0, 600, 785, 81],
+          "rect": [0, 600, 785, 145],
           "reason": "incremental"
         },
         {
@@ -27,13 +37,18 @@
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow H1",
+          "rect": [8, 700, 600, 37],
+          "reason": "became visible"
+        },
+        {
           "object": "LayoutText #text",
           "rect": [8, 8, 346, 18],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutView #document",
-          "rect": [785, 0, 15, 681],
+          "rect": [785, 0, 15, 745],
           "reason": "scroll"
         },
         {
@@ -50,6 +65,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -84,6 +103,18 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow H1",
+      "reason": "became visible"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "became visible"
     }
   ]
 }
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 8dd0107..150f6e08 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -2487,9 +2487,8 @@
 
   frameHost()->eventHandlerRegistry().documentDetached(*this);
 
-  // Since |Document| class has multiple |LifecycleNotifier| as base class,
-  // we need to have |static_cast<SynchronousMutationNotifier>| here.
-  static_cast<SynchronousMutationNotifier*>(this)->notifyContextDestroyed();
+  // Signal destruction to mutation observers.
+  SynchronousMutationNotifier::notifyContextDestroyed();
   m_frame->selection().documentDetached(*this);
 
   // If this Document is associated with a live DocumentLoader, the
@@ -4947,17 +4946,31 @@
 
 KURL Document::completeURL(const String& url) const {
   String trimmed = url.stripWhiteSpace();
+  KURL completed = completeURLWithOverride(url, m_baseURL);
+
   bool newline = trimmed.contains('\n') || trimmed.contains('\r');
-  bool brace = trimmed.contains('<');
-  if (newline)
-    UseCounter::count(*this, UseCounter::DocumentCompleteURLContainingNewline);
-  if (brace) {
-    UseCounter::count(*this,
-                      UseCounter::DocumentCompleteURLContainingOpenBrace);
-  }
-  if (newline && brace) {
+  bool lessThan = trimmed.contains('<');
+  if ((newline || lessThan) && completed.protocolIsInHTTPFamily()) {
+    if (newline) {
+      UseCounter::count(*this,
+                        UseCounter::DocumentCompleteURLHTTPContainingNewline);
+    }
+    if (lessThan) {
+      UseCounter::count(*this,
+                        UseCounter::DocumentCompleteURLHTTPContainingLessThan);
+    }
+    if (newline && lessThan) {
+      UseCounter::count(
+          *this,
+          UseCounter::DocumentCompleteURLHTTPContainingNewlineAndLessThan);
+
+      if (RuntimeEnabledFeatures::restrictCompleteURLCharacterSetEnabled())
+        return KURL();
+    }
+  } else if (newline || lessThan) {
     UseCounter::count(
-        *this, UseCounter::DocumentCompleteURLContainingNewlineAndOpenBrace);
+        *this,
+        UseCounter::DocumentCompleteURLNonHTTPContainingNewlineOrLessThan);
   }
   return completeURLWithOverride(url, m_baseURL);
 }
diff --git a/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp b/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp
index 6d6e8af3..64c7232 100644
--- a/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp
+++ b/third_party/WebKit/Source/core/dom/IdleDeadlineTest.cpp
@@ -12,11 +12,29 @@
 namespace blink {
 namespace {
 
-class MockScheduler final : public TestingPlatformMockScheduler {
+class MockScheduler final : public WebScheduler {
  public:
   MockScheduler() {}
   ~MockScheduler() override {}
+
+  // WebScheduler implementation:
+  WebTaskRunner* loadingTaskRunner() override { return nullptr; }
+  WebTaskRunner* timerTaskRunner() override { return nullptr; }
+  void shutdown() override {}
   bool shouldYieldForHighPriorityWork() override { return true; }
+  bool canExceedIdleDeadlineIfRequired() override { return false; }
+  void postIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override {}
+  void postNonNestableIdleTask(const WebTraceLocation&,
+                               WebThread::IdleTask*) override {}
+  std::unique_ptr<WebViewScheduler> createWebViewScheduler(
+      InterventionReporter*,
+      WebViewScheduler::WebViewSchedulerSettings*) override {
+    return nullptr;
+  }
+  void suspendTimerQueue() override {}
+  void resumeTimerQueue() override {}
+  void addPendingNavigation(WebScheduler::NavigatingFrameType) override {}
+  void removePendingNavigation(WebScheduler::NavigatingFrameType) override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockScheduler);
diff --git a/third_party/WebKit/Source/core/fetch/MemoryCacheCorrectnessTest.cpp b/third_party/WebKit/Source/core/fetch/MemoryCacheCorrectnessTest.cpp
index 5da4703..04c63252 100644
--- a/third_party/WebKit/Source/core/fetch/MemoryCacheCorrectnessTest.cpp
+++ b/third_party/WebKit/Source/core/fetch/MemoryCacheCorrectnessTest.cpp
@@ -232,7 +232,7 @@
   EXPECT_NE(fresh200Nocache, fetched);
 }
 
-TEST_F(MemoryCacheCorrectnessTest, RequestWithNoCahe) {
+TEST_F(MemoryCacheCorrectnessTest, RequestWithNoCache) {
   ResourceRequest noCacheRequest;
   noCacheRequest.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
   Resource* noCacheResource = resourceFromResourceRequest(noCacheRequest);
diff --git a/third_party/WebKit/Source/core/fetch/RawResourceTest.cpp b/third_party/WebKit/Source/core/fetch/RawResourceTest.cpp
index 501cf024..acc22a82 100644
--- a/third_party/WebKit/Source/core/fetch/RawResourceTest.cpp
+++ b/third_party/WebKit/Source/core/fetch/RawResourceTest.cpp
@@ -146,24 +146,26 @@
 
  public:
   AddingClient(DummyClient* client, Resource* resource)
-      : m_dummyClient(client),
-        m_resource(resource),
-        m_removeClientTimer(this, &AddingClient::removeClient) {}
+      : m_dummyClient(client), m_resource(resource) {}
 
   ~AddingClient() override {}
 
   // ResourceClient implementation.
   void notifyFinished(Resource* resource) override {
     // First schedule an asynchronous task to remove the client.
-    // We do not expect the client to be called.
-    m_removeClientTimer.startOneShot(0, BLINK_FROM_HERE);
+    // We do not expect a client to be called if the client is removed before
+    // a callback invocation task queued inside addClient() is scheduled.
+    Platform::current()
+        ->currentThread()
+        ->scheduler()
+        ->loadingTaskRunner()
+        ->postTask(BLINK_FROM_HERE, WTF::bind(&AddingClient::removeClient,
+                                              wrapPersistent(this)));
     resource->addClient(m_dummyClient);
   }
   String debugName() const override { return "AddingClient"; }
 
-  void removeClient(TimerBase* timer) {
-    m_resource->removeClient(m_dummyClient);
-  }
+  void removeClient() { m_resource->removeClient(m_dummyClient); }
 
   DEFINE_INLINE_VIRTUAL_TRACE() {
     visitor->trace(m_dummyClient);
@@ -174,7 +176,6 @@
  private:
   Member<DummyClient> m_dummyClient;
   Member<Resource> m_resource;
-  Timer<AddingClient> m_removeClientTimer;
 };
 
 TEST(RawResourceTest, RevalidationSucceeded) {
diff --git a/third_party/WebKit/Source/core/frame/Frame.cpp b/third_party/WebKit/Source/core/frame/Frame.cpp
index 6f7a7402..d259c5d 100644
--- a/third_party/WebKit/Source/core/frame/Frame.cpp
+++ b/third_party/WebKit/Source/core/frame/Frame.cpp
@@ -82,7 +82,13 @@
 
 void Frame::disconnectOwnerElement() {
   if (m_owner) {
-    m_owner->clearContentFrame();
+    // Ocassionally, provisional frames need to be detached, but it shouldn't
+    // affect the frame tree structure. Make sure the frame owner's content
+    // frame actually refers to this frame before clearing it.
+    // TODO(dcheng): https://crbug.com/578349 tracks the cleanup for this once
+    // it's no longer needed.
+    if (m_owner->contentFrame() == this)
+      m_owner->clearContentFrame();
     m_owner = nullptr;
   }
 }
diff --git a/third_party/WebKit/Source/core/frame/FrameOwner.h b/third_party/WebKit/Source/core/frame/FrameOwner.h
index 352d8ed4f..6149324 100644
--- a/third_party/WebKit/Source/core/frame/FrameOwner.h
+++ b/third_party/WebKit/Source/core/frame/FrameOwner.h
@@ -27,6 +27,7 @@
   virtual bool isLocal() const = 0;
   virtual bool isRemote() const = 0;
 
+  virtual Frame* contentFrame() const = 0;
   virtual void setContentFrame(Frame&) = 0;
   virtual void clearContentFrame() = 0;
 
@@ -47,6 +48,9 @@
   virtual const WebVector<WebPermissionType>& delegatedPermissions() const = 0;
 };
 
+// TODO(dcheng): This class is an internal implementation detail of provisional
+// frames. Move this into WebLocalFrameImpl.cpp and remove existing dependencies
+// on it.
 class CORE_EXPORT DummyFrameOwner
     : public GarbageCollectedFinalized<DummyFrameOwner>,
       public FrameOwner {
@@ -58,6 +62,7 @@
   DEFINE_INLINE_VIRTUAL_TRACE() { FrameOwner::trace(visitor); }
 
   // FrameOwner overrides:
+  Frame* contentFrame() const override { return nullptr; }
   void setContentFrame(Frame&) override {}
   void clearContentFrame() override {}
   SandboxFlags getSandboxFlags() const override { return SandboxNone; }
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index dd8b614..2fa053d0 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1435,9 +1435,10 @@
     V8AssigmentExpressionLHSIsCallInStrict = 1765,
     V8PromiseConstructorReturnedUndefined = 1766,
     FormSubmittedWithUnclosedFormControl = 1767,
-    DocumentCompleteURLContainingNewline = 1768,
-    DocumentCompleteURLContainingOpenBrace = 1769,
-    DocumentCompleteURLContainingNewlineAndOpenBrace = 1770,
+    DocumentCompleteURLHTTPContainingNewline = 1768,
+    DocumentCompleteURLHTTPContainingLessThan = 1769,
+    DocumentCompleteURLHTTPContainingNewlineAndLessThan = 1770,
+    DocumentCompleteURLNonHTTPContainingNewlineOrLessThan = 1771,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.h b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.h
index 2369fe5..b478452 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameElementBase.h
+++ b/third_party/WebKit/Source/core/html/HTMLFrameElementBase.h
@@ -34,9 +34,9 @@
   bool canContainRangeEndPoint() const final { return false; }
 
   // FrameOwner overrides:
-  ScrollbarMode scrollingMode() const override { return m_scrollingMode; }
-  int marginWidth() const override { return m_marginWidth; }
-  int marginHeight() const override { return m_marginHeight; }
+  ScrollbarMode scrollingMode() const final { return m_scrollingMode; }
+  int marginWidth() const final { return m_marginWidth; }
+  int marginHeight() const final { return m_marginHeight; }
 
  protected:
   HTMLFrameElementBase(const QualifiedName&, Document&);
diff --git a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
index 6e9a4b3..bd608f31 100644
--- a/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.h
@@ -44,7 +44,6 @@
  public:
   ~HTMLFrameOwnerElement() override;
 
-  Frame* contentFrame() const { return m_contentFrame; }
   DOMWindow* contentWindow() const;
   Document* contentDocument() const;
 
@@ -76,10 +75,11 @@
   };
 
   // FrameOwner overrides:
-  void setContentFrame(Frame&) override;
-  void clearContentFrame() override;
-  void dispatchLoad() override;
-  SandboxFlags getSandboxFlags() const override { return m_sandboxFlags; }
+  Frame* contentFrame() const final { return m_contentFrame; }
+  void setContentFrame(Frame&) final;
+  void clearContentFrame() final;
+  void dispatchLoad() final;
+  SandboxFlags getSandboxFlags() const final { return m_sandboxFlags; }
   bool canRenderFallbackContent() const override { return false; }
   void renderFallbackContent() override {}
   ScrollbarMode scrollingMode() const override { return ScrollbarAuto; }
@@ -106,8 +106,8 @@
  private:
   // Intentionally private to prevent redundant checks when the type is
   // already HTMLFrameOwnerElement.
-  bool isLocal() const override { return true; }
-  bool isRemote() const override { return false; }
+  bool isLocal() const final { return true; }
+  bool isRemote() const final { return false; }
 
   bool isFrameOwnerElement() const final { return true; }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.h b/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
index c49204a..51ae060 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.h
@@ -73,11 +73,10 @@
 
   ReferrerPolicy referrerPolicyAttribute() override;
 
+  // FrameOwner overrides:
   bool allowFullscreen() const override { return m_allowFullscreen; }
   bool allowPaymentRequest() const override { return m_allowPaymentRequest; }
-
   AtomicString csp() const override { return m_csp; }
-
   const WebVector<WebPermissionType>& delegatedPermissions() const override {
     return m_delegatedPermissions;
   }
diff --git a/third_party/WebKit/Source/core/html/forms/SearchInputType.cpp b/third_party/WebKit/Source/core/html/forms/SearchInputType.cpp
index 7ec269b6..97b1f0f7 100644
--- a/third_party/WebKit/Source/core/html/forms/SearchInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/SearchInputType.cpp
@@ -48,7 +48,10 @@
 
 inline SearchInputType::SearchInputType(HTMLInputElement& element)
     : BaseTextInputType(element),
-      m_searchEventTimer(this, &SearchInputType::searchEventTimerFired) {}
+      m_searchEventTimer(
+          TaskRunnerHelper::get(TaskType::UserInteraction, &element.document()),
+          this,
+          &SearchInputType::searchEventTimerFired) {}
 
 InputType* SearchInputType::create(HTMLInputElement& element) {
   return new SearchInputType(element);
diff --git a/third_party/WebKit/Source/core/html/forms/SearchInputType.h b/third_party/WebKit/Source/core/html/forms/SearchInputType.h
index 63fed234..be0efe9 100644
--- a/third_party/WebKit/Source/core/html/forms/SearchInputType.h
+++ b/third_party/WebKit/Source/core/html/forms/SearchInputType.h
@@ -59,7 +59,7 @@
   void startSearchEventTimer();
   void updateCancelButtonVisibility();
 
-  Timer<SearchInputType> m_searchEventTimer;
+  TaskRunnerTimer<SearchInputType> m_searchEventTimer;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
index 85aa7103..d30f1f5 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -286,3 +286,4 @@
 PerformancePaintTiming status=test
 HideNonceContentAttribute status=experimental
 UnclosedFormControlIsInvalid status=experimental
+RestrictCompleteURLCharacterSet status=experimental
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
index d99808b..bf236c6 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
@@ -131,9 +131,6 @@
   return nullptr;
 }
 
-TestingPlatformMockScheduler::TestingPlatformMockScheduler() {}
-TestingPlatformMockScheduler::~TestingPlatformMockScheduler() {}
-
 TestingPlatformSupport::TestingPlatformSupport()
     : TestingPlatformSupport(TestingPlatformSupport::Config()) {}
 
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
index cf90b81..1f816639 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
@@ -84,33 +84,6 @@
       bool isLeftSideVerticalScrollbar) override;
 };
 
-class TestingPlatformMockScheduler : public WebScheduler {
-  WTF_MAKE_NONCOPYABLE(TestingPlatformMockScheduler);
-
- public:
-  TestingPlatformMockScheduler();
-  ~TestingPlatformMockScheduler() override;
-
-  // WebScheduler implementation:
-  WebTaskRunner* loadingTaskRunner() override { return nullptr; }
-  WebTaskRunner* timerTaskRunner() override { return nullptr; }
-  void shutdown() override {}
-  bool shouldYieldForHighPriorityWork() override { return false; }
-  bool canExceedIdleDeadlineIfRequired() override { return false; }
-  void postIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override {}
-  void postNonNestableIdleTask(const WebTraceLocation&,
-                               WebThread::IdleTask*) override {}
-  std::unique_ptr<WebViewScheduler> createWebViewScheduler(
-      InterventionReporter*,
-      WebViewScheduler::WebViewSchedulerSettings*) override {
-    return nullptr;
-  }
-  void suspendTimerQueue() override {}
-  void resumeTimerQueue() override {}
-  void addPendingNavigation(WebScheduler::NavigatingFrameType) override {}
-  void removePendingNavigation(WebScheduler::NavigatingFrameType) override {}
-};
-
 // A base class to override Platform methods for testing.  You can override the
 // behavior by subclassing TestingPlatformSupport or using
 // ScopedTestingPlatformSupport (see below).
diff --git a/third_party/WebKit/Source/web/RemoteFrameOwner.h b/third_party/WebKit/Source/web/RemoteFrameOwner.h
index 25d3977..5cce8db 100644
--- a/third_party/WebKit/Source/web/RemoteFrameOwner.h
+++ b/third_party/WebKit/Source/web/RemoteFrameOwner.h
@@ -29,6 +29,7 @@
   }
 
   // FrameOwner overrides:
+  Frame* contentFrame() const override { return m_frame.get(); }
   void setContentFrame(Frame&) override;
   void clearContentFrame() override;
   SandboxFlags getSandboxFlags() const override { return m_sandboxFlags; }
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
index a6ea7004..f249400a 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
@@ -289,6 +289,26 @@
 
 TestWebFrameClient::TestWebFrameClient() {}
 
+// TODO(dcheng): https://crbug.com/578349 tracks the removal of this code. This
+// override exists only to handle the confusing provisional frame case.
+void TestWebFrameClient::frameDetached(WebLocalFrame* frame, DetachType type) {
+  if (type == DetachType::Remove && frame->parent()) {
+    // Since this may be detaching a provisional frame, make sure |child| is
+    // actually linked into the frame tree (i.e. it is present in its parent
+    // node's children list) before trying to remove it as a child.
+    for (WebFrame* child = frame->parent()->firstChild(); child;
+         child = child->nextSibling()) {
+      if (child == frame)
+        frame->parent()->removeChild(frame);
+    }
+  }
+
+  if (frame->frameWidget())
+    frame->frameWidget()->close();
+
+  frame->close();
+}
+
 WebLocalFrame* TestWebFrameClient::createChildFrame(
     WebLocalFrame* parent,
     WebTreeScopeType scope,
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
index 35e217c..67c41b3da 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
@@ -237,6 +237,7 @@
  public:
   TestWebFrameClient();
 
+  void frameDetached(WebLocalFrame*, DetachType) override;
   WebLocalFrame* createChildFrame(WebLocalFrame* parent,
                                   WebTreeScopeType,
                                   const WebString& name,
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index e280ce9..853ab22 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -8729,6 +8729,38 @@
   remoteFrame->close();
 }
 
+TEST_F(WebFrameSwapTest, DetachProvisionalFrame) {
+  FrameTestHelpers::TestWebRemoteFrameClient remoteFrameClient;
+  WebRemoteFrameImpl* remoteFrame = WebRemoteFrameImpl::create(
+      WebTreeScopeType::Document, &remoteFrameClient);
+  swapAndVerifyMiddleChildConsistency("local->remote", mainFrame(),
+                                      remoteFrame);
+
+  FrameTestHelpers::TestWebFrameClient client;
+  WebLocalFrameImpl* provisionalFrame = WebLocalFrameImpl::createProvisional(
+      &client, remoteFrame, WebSandboxFlags::None);
+
+  // The provisional frame should have a local frame owner.
+  FrameOwner* owner = provisionalFrame->frame()->owner();
+  ASSERT_TRUE(owner->isLocal());
+
+  // But the owner should point to |remoteFrame|, since the new frame is still
+  // provisional.
+  EXPECT_EQ(remoteFrame->frame(), owner->contentFrame());
+
+  // After detaching the provisional frame, the frame owner should still point
+  // at |remoteFrame|.
+  provisionalFrame->detach();
+
+  // The owner should not be affected by detaching the provisional frame, so it
+  // should still point to |remoteFrame|.
+  EXPECT_EQ(remoteFrame->frame(), owner->contentFrame());
+
+  // Manually reset to break WebViewHelper's dependency on the stack allocated
+  // TestWebFrameClient.
+  reset();
+}
+
 void WebFrameTest::swapAndVerifySubframeConsistency(const char* const message,
                                                     WebFrame* oldFrame,
                                                     WebFrame* newFrame) {
diff --git a/third_party/WebKit/Source/wtf/text/StringImpl.cpp b/third_party/WebKit/Source/wtf/text/StringImpl.cpp
index f8b6b7b..6654e30 100644
--- a/third_party/WebKit/Source/wtf/text/StringImpl.cpp
+++ b/third_party/WebKit/Source/wtf/text/StringImpl.cpp
@@ -335,7 +335,7 @@
 
 #if DCHECK_IS_ON()
 std::string StringImpl::asciiForDebugging() const {
-  CString ascii = String(substring(0, 128)).ascii();
+  CString ascii = String(isolatedCopy()->substring(0, 128)).ascii();
   return std::string(ascii.data(), ascii.length());
 }
 #endif
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 50f78f2b..f050d49 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -48,11 +48,23 @@
                                             WebFrameClient*,
                                             WebFrame* opener = nullptr);
 
-  // Used to create a provisional local frame in prepration for replacing a
-  // remote frame if the load commits. The returned frame is only partially
-  // attached to the frame tree: it has the same parent as its potential
-  // replacee but is invisible to the rest of the frames in the frame tree.
-  // If the load commits, call swap() to fully attach this frame.
+  // Used to create a provisional local frame. Currently, it's possible for a
+  // provisional navigation not to commit (i.e. it might turn into a download),
+  // but this can only be determined by actually trying to load it. The loading
+  // process depends on having a corresponding LocalFrame in Blink to hold all
+  // the pending state.
+  //
+  // When a provisional frame is first created, it is only partially attached to
+  // the frame tree. This means that though a provisional frame might have a
+  // frame owner, the frame owner's content frame does not point back at the
+  // provisional frame. Similarly, though a provisional frame may have a parent
+  // frame pointer, the parent frame's children list will not contain the
+  // provisional frame. Thus, a provisional frame is invisible to the rest of
+  // Blink unless the navigation commits and the provisional frame is fully
+  // attached to the frame tree by calling swap().
+  //
+  // Otherwise, if the load should not commit, call detach() to discard the
+  // frame.
   BLINK_EXPORT static WebLocalFrame* createProvisional(WebFrameClient*,
                                                        WebRemoteFrame*,
                                                        WebSandboxFlags);
diff --git a/tools/licenses.py b/tools/licenses.py
index 9bdc018..45e783e 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -185,7 +185,7 @@
     os.path.join('third_party', 'WebKit'): {
         "Name": "WebKit",
         "URL": "http://webkit.org/",
-        "License": "BSD and GPL v2",
+        "License": "BSD and LGPL v2 and LGPL v2.1",
         # Absolute path here is resolved as relative to the source root.
         "License File": "/third_party/WebKit/LICENSE_FOR_ABOUT_CREDITS",
     },
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 11f243c..efdf0cbe 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -89107,9 +89107,12 @@
   <int value="1765" label="V8AssigmentExpressionLHSIsCallInStrict"/>
   <int value="1766" label="V8PromiseConstructorReturnedUndefined"/>
   <int value="1767" label="FormSubmittedWithUnclosedFormControl"/>
-  <int value="1768" label="DocumentCompleteURLContainingNewline"/>
-  <int value="1769" label="DocumentCompleteURLContainingOpenBrace"/>
-  <int value="1770" label="DocumentCompleteURLContainingNewlineAndOpenBrace"/>
+  <int value="1768" label="DocumentCompleteURLHTTPContainingNewline"/>
+  <int value="1769" label="DocumentCompleteURLHTTPContainingLessThan"/>
+  <int value="1770"
+      label="DocumentCompleteURLHTTPContainingNewlineAndLessThan"/>
+  <int value="1771"
+      label="DocumentCompleteURLNonHTTPContainingNewlineOrLessThan"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index 4eb83b90..b0eafac 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -41,6 +41,15 @@
         chrome_trace_config.MemoryDumpConfig())
     return tbm_options
 
+  def SetExtraBrowserOptions(self, options):
+    # Just before we measure memory we flush the system caches
+    # unfortunately this doesn't immediately take effect, instead
+    # the next page run is effected. Due to this the first page run
+    # has anomalous results. This option causes us to flush caches
+    # each time before Chrome starts so we effect even the first page
+    # - avoiding the bug.
+    options.clear_sytem_cache_for_browser_and_profile_on_start = True
+
 
 # TODO(bashi): Workaround for http://crbug.com/532075.
 # @benchmark.Enabled('android') shouldn't be needed.
diff --git a/tools/perf/benchmarks/system_health.py b/tools/perf/benchmarks/system_health.py
index 8053919..75a68813 100644
--- a/tools/perf/benchmarks/system_health.py
+++ b/tools/perf/benchmarks/system_health.py
@@ -105,6 +105,15 @@
     return page_sets.SystemHealthStorySet(platform=self.PLATFORM,
                                           take_memory_measurement=True)
 
+  def SetExtraBrowserOptions(self, options):
+    # Just before we measure memory we flush the system caches
+    # unfortunately this doesn't immediately take effect, instead
+    # the next story run is effected. Due to this the first story run
+    # has anomalous results. This option causes us to flush caches
+    # each time before Chrome starts so we effect even the first story
+    # - avoiding the bug.
+    options.clear_sytem_cache_for_browser_and_profile_on_start = True
+
   @classmethod
   def ShouldTearDownStateAfterEachStoryRun(cls):
     return True
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index b9d1a27..63769da4 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -22,6 +22,8 @@
 
   IS_SINGLE_PAGE_APP = False
   ITEM_SELECTOR = NotImplemented
+  # Defaults to using the body element if not set.
+  CONTAINER_SELECTOR = None
   ABSTRACT_STORY = True
 
   def _WaitForNavigation(self, action_runner):
@@ -33,7 +35,9 @@
         self.ITEM_SELECTOR, index)
     # Only scrolls if element is not currently in viewport.
     action_runner.WaitForElement(element_function=item_selector)
-    action_runner.ScrollPageToElement(element_function=item_selector)
+    action_runner.ScrollPageToElement(
+        element_function=item_selector,
+        container_selector=self.CONTAINER_SELECTOR)
     self._ClickLink(action_runner, item_selector)
 
   def _ClickLink(self, action_runner, element_function):
@@ -195,6 +199,7 @@
   NAME = 'browse:social:twitter'
   URL = 'https://www.twitter.com/nasa'
   ITEM_SELECTOR = '.Tweet-text'
+  CONTAINER_SELECTOR = '.NavigationSheet'
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
 
 
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources2.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources2.gyp
index 5324e57..a9f45f16 100644
--- a/ui/file_manager/file_manager/foreground/js/compiled_resources2.gyp
+++ b/ui/file_manager/file_manager/foreground/js/compiled_resources2.gyp
@@ -147,10 +147,14 @@
 #      'target_name': 'quick_view_controller',
 #      'includes': ['../../../compile_js2.gypi'],
 #    },
-#    {
-#      'target_name': 'quick_view_model',
-#      'includes': ['../../../compile_js2.gypi'],
-#    },
+     {
+       'target_name': 'quick_view_model',
+       'dependencies': [
+         '../../../../../ui/webui/resources/js/compiled_resources2.gyp:cr',
+         '../../../../../ui/webui/resources/js/cr/compiled_resources2.gyp:event_target',
+       ],
+       'includes': ['../../../compile_js2.gypi'],
+     },
 #    {
 #      'target_name': 'quick_view_uma',
 #      'includes': ['../../../compile_js2.gypi'],
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 667e70aa..f779b039 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -548,7 +548,8 @@
         assert(this.metadataModel_), assert(this.selectionHandler_),
         assert(this.ui_.listContainer), assert(this.quickViewModel_),
         assert(this.taskController_), fileListSelectionModel,
-        assert(this.quickViewUma_), metadataBoxController, this.dialogType);
+        assert(this.quickViewUma_), metadataBoxController, this.dialogType,
+        assert(this.volumeManager_));
 
     if (this.dialogType === DialogType.FULL_PAGE) {
       importer.importEnabled().then(
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index f659b30..1b8e182b 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -15,13 +15,14 @@
  * @param {!QuickViewUma} quickViewUma
  * @param {!MetadataBoxController} metadataBoxController
  * @param {DialogType} dialogType
+ * @param {!VolumeManagerWrapper} volumeManager
  *
  * @constructor
  */
 function QuickViewController(
     metadataModel, selectionHandler, listContainer, quickViewModel,
     taskController, fileListSelectionModel, quickViewUma,
-    metadataBoxController, dialogType) {
+    metadataBoxController, dialogType, volumeManager) {
   /**
    * @type {FilesQuickView}
    * @private
@@ -83,6 +84,12 @@
   this.dialogType_ = dialogType;
 
   /**
+   * @type {!VolumeManagerWrapper}
+   * @private
+   */
+  this.volumeManager_ = volumeManager;
+
+  /**
    * Current selection of selectionHandler.
    *
    * @type {!Array<!FileEntry>}
@@ -102,6 +109,28 @@
 }
 
 /**
+ * List of local volume types.
+ *
+ * In this context, "local" means that files in that volume are directly
+ * accessible from the Chrome browser process by Linux VFS paths. In this
+ * regard, media views are NOT local even though files in media views are
+ * actually stored in the local disk.
+ *
+ * Due to access control of WebView, non-local files can not be previewed
+ * with Quick View unless thumbnails are provided (which is the case with
+ * Drive).
+ *
+ * @type {!Array<!VolumeManagerCommon.VolumeType>}
+ * @const
+ * @private
+ */
+QuickViewController.LOCAL_VOLUME_TYPES_ = [
+  VolumeManagerCommon.VolumeType.ARCHIVE,
+  VolumeManagerCommon.VolumeType.DOWNLOADS,
+  VolumeManagerCommon.VolumeType.REMOVABLE,
+];
+
+/**
  * Initialize the controller with quick view which will be lazily loaded.
  * @param {!FilesQuickView} quickView
  * @private
@@ -258,7 +287,7 @@
   this.quickViewUma_.onEntryChanged(entry);
   return Promise
       .all([
-        this.metadataModel_.get([entry], ['thumbnailUrl', 'externalFileUrl']),
+        this.metadataModel_.get([entry], ['thumbnailUrl']),
         this.getAvailableTasks_(entry)
       ])
       .then(function(values) {
@@ -325,7 +354,13 @@
     hasTask: tasks.length > 0,
   };
 
-  if (item.externalFileUrl) {
+  var volumeInfo = this.volumeManager_.getVolumeInfo(entry);
+  var localFile =
+      volumeInfo &&
+      QuickViewController.LOCAL_VOLUME_TYPES_.indexOf(
+          volumeInfo.volumeType) >= 0;
+
+  if (!localFile) {
     switch (type) {
       case 'image':
         if (item.thumbnailUrl) {
@@ -349,7 +384,7 @@
         }
         break;
     }
-    // If the file is in Drive, we ask user to open it with external app.
+    // We ask user to open it with external app.
     return Promise.resolve(params);
   }
 
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 5ca41e2f..1ec9dec 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -34,6 +34,8 @@
 /** @const */ var SCREEN_DEVICE_DISABLED = 'device-disabled';
 /** @const */ var SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR =
     'unrecoverable-cryptohome-error';
+/** @const */ var SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE =
+    'ad-password-change';
 
 /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */
 /** @const */ var ACCELERATOR_CANCEL = 'cancel';
@@ -1003,6 +1005,15 @@
   };
 
   /**
+   * Shows password change screen for Active Directory users.
+   * @param {string} username Display name of the user whose password is being
+   * changed.
+   */
+  DisplayManager.showActiveDirectoryPasswordChangeScreen = function(username) {
+    login.ActiveDirectoryPasswordChangeScreen.show(username);
+  };
+
+  /**
    * Clears error bubble.
    */
   DisplayManager.clearErrors = function() {
diff --git a/ui/login/screen_container.css b/ui/login/screen_container.css
index 2dd778817..ae57e49 100644
--- a/ui/login/screen_container.css
+++ b/ui/login/screen_container.css
@@ -99,6 +99,7 @@
 #oobe.kiosk-enable #inner-container,
 #oobe.oauth-enrollment #inner-container,
 #oobe.password-changed #inner-container,
+#oobe.ad-password-change #inner-container,
 #oobe.reset #inner-container,
 #oobe.supervised-user-creation #inner-container,
 #oobe.supervised-user-creation-dialog #inner-container,