diff --git a/DEPS b/DEPS
index 4bfd39b6..b97b3e4 100644
--- a/DEPS
+++ b/DEPS
@@ -39,11 +39,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': 'e9573317d35d08254412eb407211f3607f8f74fb',
+  'skia_revision': 'eae84c2e0e2126374cd488a1c8a3e18169145635',
   # 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': 'e78b4e2e8eef9db0bd74d2dc2d8b0927e1004cdf',
+  'v8_revision': '2801e08d8f527caaa58b07607ac0fbc04aa998f7',
   # 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.
@@ -184,7 +184,7 @@
    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'c0307e6cea0fcd79577eaa107f76b07acaf1d4e6',
 
   'src/third_party/ffmpeg':
-   Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '2dc5618f9b6f04d4ba4cda56350051c815841e34',
+   Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '2ab535183e2fca15d37be86bf0edf5aa8c6767cf',
 
   'src/third_party/libjingle/source/talk':
     Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + 'b62ca7e76805b8d87795c73fcdd5dd1457c14461', # commit position 11358
@@ -271,7 +271,7 @@
 
   'src/third_party/catapult':
     Var('chromium_git') + '/external/github.com/catapult-project/catapult.git' + '@' +
-    'f7563a0c32bf3a59ff49150461bafbfe0082f17b',
+    '4028a4f07d37b511af1052485fe9097b9e4cabdf',
 
   'src/third_party/openh264/src':
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'b37cda248234162033e3e11b0335f3131cdfe488',
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5e94b37..3f8380c7 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -849,11 +849,8 @@
     "trace_event/process_memory_dump.h",
     "trace_event/process_memory_maps.cc",
     "trace_event/process_memory_maps.h",
-    "trace_event/process_memory_maps_dump_provider.h",
     "trace_event/process_memory_totals.cc",
     "trace_event/process_memory_totals.h",
-    "trace_event/process_memory_totals_dump_provider.cc",
-    "trace_event/process_memory_totals_dump_provider.h",
     "trace_event/trace_buffer.cc",
     "trace_event/trace_buffer.h",
     "trace_event/trace_config.cc",
@@ -994,7 +991,6 @@
       "sys_info_linux.cc",
       "trace_event/malloc_dump_provider.cc",
       "trace_event/malloc_dump_provider.h",
-      "trace_event/process_memory_maps_dump_provider.cc",
     ]
     set_sources_assignment_filter(sources_assignment_filter)
 
@@ -1061,7 +1057,6 @@
       "sync_socket_posix.cc",
       "sys_info.cc",
       "sys_info_posix.cc",
-      "trace_event/process_memory_totals_dump_provider.cc",
       "trace_event/trace_event_system_stats_monitor.cc",
     ]
 
@@ -1190,7 +1185,6 @@
     sources += [
       "trace_event/malloc_dump_provider.cc",
       "trace_event/malloc_dump_provider.h",
-      "trace_event/process_memory_maps_dump_provider.cc",
     ]
 
     if (is_asan || is_lsan || is_msan || is_tsan) {
@@ -1828,7 +1822,6 @@
     "trace_event/memory_allocator_dump_unittest.cc",
     "trace_event/memory_dump_manager_unittest.cc",
     "trace_event/process_memory_dump_unittest.cc",
-    "trace_event/process_memory_totals_dump_provider_unittest.cc",
     "trace_event/trace_config_memory_test_util.h",
     "trace_event/trace_config_unittest.cc",
     "trace_event/trace_event_argument_unittest.cc",
@@ -1945,10 +1938,6 @@
     }
   }
 
-  if (is_linux || is_android) {
-    sources += [ "trace_event/process_memory_maps_dump_provider_unittest.cc" ]
-  }
-
   if (!is_linux || use_ozone) {
     sources -= [ "message_loop/message_pump_glib_unittest.cc" ]
   }
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index bcebcf5..7a731bb 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -86,7 +86,8 @@
       return value;
     }
   }
-  NOTREACHED();
+  // This can be reached if the process dies when proc is read -- in that case,
+  // the kernel can return missing fields.
   return 0;
 }
 
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 156f3247..a0f0af37 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -23,14 +23,6 @@
 #include "base/trace_event/trace_event_argument.h"
 #include "build/build_config.h"
 
-#if !defined(OS_NACL)
-#include "base/trace_event/process_memory_totals_dump_provider.h"
-#endif
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-#include "base/trace_event/process_memory_maps_dump_provider.h"
-#endif
-
 #if defined(OS_ANDROID)
 #include "base/trace_event/java_heap_dump_provider_android.h"
 #endif
@@ -166,20 +158,10 @@
   }
 
 // Enable the core dump providers.
-#if !defined(OS_NACL)
-  RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance(),
-                       "ProcessMemoryTotals", nullptr);
-#endif
-
 #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
   RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
 #endif
 
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-  RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance(),
-                       "ProcessMemoryMaps", nullptr);
-#endif
-
 #if defined(OS_ANDROID)
   RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
                        nullptr);
diff --git a/base/trace_event/process_memory_maps_dump_provider.cc b/base/trace_event/process_memory_maps_dump_provider.cc
deleted file mode 100644
index 4c3959f..0000000
--- a/base/trace_event/process_memory_maps_dump_provider.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/trace_event/process_memory_maps_dump_provider.h"
-
-#include <stdint.h>
-
-#include "base/files/scoped_file.h"
-#include "base/format_macros.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/process_memory_maps.h"
-
-namespace base {
-namespace trace_event {
-
-// static
-FILE* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
-
-namespace {
-
-const uint32_t kMaxLineSize = 4096;
-
-bool ParseSmapsHeader(const char* header_line,
-                      ProcessMemoryMaps::VMRegion* region) {
-  // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234  /foo.so\n"
-  bool res = true;  // Whether this region should be appended or skipped.
-  uint64_t end_addr = 0;
-  char protection_flags[5] = {0};
-  char mapped_file[kMaxLineSize];
-
-  if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
-             &region->start_address, &end_addr, protection_flags,
-             mapped_file) != 4)
-    return false;
-
-  if (end_addr > region->start_address) {
-    region->size_in_bytes = end_addr - region->start_address;
-  } else {
-    // This is not just paranoia, it can actually happen (See crbug.com/461237).
-    region->size_in_bytes = 0;
-    res = false;
-  }
-
-  region->protection_flags = 0;
-  if (protection_flags[0] == 'r') {
-    region->protection_flags |=
-        ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
-  }
-  if (protection_flags[1] == 'w') {
-    region->protection_flags |=
-        ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
-  }
-  if (protection_flags[2] == 'x') {
-    region->protection_flags |=
-        ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
-  }
-
-  region->mapped_file = mapped_file;
-  TrimWhitespaceASCII(region->mapped_file, TRIM_ALL, &region->mapped_file);
-
-  return res;
-}
-
-uint64_t ReadCounterBytes(char* counter_line) {
-  uint64_t counter_value = 0;
-  int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
-  DCHECK_EQ(1, res);
-  return counter_value * 1024;
-}
-
-uint32_t ParseSmapsCounter(char* counter_line,
-                           ProcessMemoryMaps::VMRegion* region) {
-  // A smaps counter lines looks as follows: "RSS:  0 Kb\n"
-  uint32_t res = 1;
-  char counter_name[20];
-  int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
-  DCHECK_EQ(1, did_read);
-
-  if (strcmp(counter_name, "Pss:") == 0) {
-    region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
-  } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
-    region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
-  } else if (strcmp(counter_name, "Private_Clean:") == 0) {
-    region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
-  } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
-    region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
-  } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
-    region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
-  } else if (strcmp(counter_name, "Swap:") == 0) {
-    region->byte_stats_swapped = ReadCounterBytes(counter_line);
-  } else {
-    res = 0;
-  }
-
-  return res;
-}
-
-uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file, ProcessMemoryMaps* pmm) {
-  if (!smaps_file)
-    return 0;
-
-  fseek(smaps_file, 0, SEEK_SET);
-
-  char line[kMaxLineSize];
-  const uint32_t kNumExpectedCountersPerRegion = 6;
-  uint32_t counters_parsed_for_current_region = 0;
-  uint32_t num_valid_regions = 0;
-  ProcessMemoryMaps::VMRegion region;
-  bool should_add_current_region = false;
-  for (;;) {
-    line[0] = '\0';
-    if (fgets(line, kMaxLineSize, smaps_file) == nullptr)
-      break;
-    DCHECK_GT(strlen(line), 0u);
-    if (isxdigit(line[0]) && !isupper(line[0])) {
-      region = ProcessMemoryMaps::VMRegion();
-      counters_parsed_for_current_region = 0;
-      should_add_current_region = ParseSmapsHeader(line, &region);
-    } else {
-      counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
-      DCHECK_LE(counters_parsed_for_current_region,
-                kNumExpectedCountersPerRegion);
-      if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
-        if (should_add_current_region) {
-          pmm->AddVMRegion(region);
-          ++num_valid_regions;
-          should_add_current_region = false;
-        }
-      }
-    }
-  }
-  return num_valid_regions;
-}
-
-}  // namespace
-
-// static
-ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() {
-  return Singleton<ProcessMemoryMapsDumpProvider,
-                   LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get();
-}
-
-ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
-}
-
-ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
-}
-
-// Called at trace dump point time. Creates a snapshot of the memory maps for
-// the current process.
-bool ProcessMemoryMapsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
-                                                 ProcessMemoryDump* pmd) {
-  // Snapshot of memory maps is not taken for light dump requests.
-  if (args.level_of_detail == MemoryDumpLevelOfDetail::LIGHT)
-    return true;
-
-  uint32_t res = 0;
-  if (UNLIKELY(proc_smaps_for_testing)) {
-    res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
-  } else {
-    ScopedFILE smaps_file(fopen("/proc/self/smaps", "r"));
-    res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
-  }
-
-  if (res > 0) {
-    pmd->set_has_process_mmaps();
-    return true;
-  }
-  return false;
-}
-
-}  // namespace trace_event
-}  // namespace base
diff --git a/base/trace_event/process_memory_maps_dump_provider.h b/base/trace_event/process_memory_maps_dump_provider.h
deleted file mode 100644
index 84badfe..0000000
--- a/base/trace_event/process_memory_maps_dump_provider.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
-#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "build/build_config.h"
-
-namespace base {
-namespace trace_event {
-
-// Dump provider which collects process-wide memory stats.
-class BASE_EXPORT ProcessMemoryMapsDumpProvider : public MemoryDumpProvider {
- public:
-  static ProcessMemoryMapsDumpProvider* GetInstance();
-
-  // MemoryDumpProvider implementation.
-  bool OnMemoryDump(const MemoryDumpArgs& args,
-                    ProcessMemoryDump* pmd) override;
-
- private:
-  friend struct DefaultSingletonTraits<ProcessMemoryMapsDumpProvider>;
-  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps);
-
-#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_NACL)
-  static FILE* proc_smaps_for_testing;
-#endif
-
-  ProcessMemoryMapsDumpProvider();
-  ~ProcessMemoryMapsDumpProvider() override;
-
-  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMapsDumpProvider);
-};
-
-}  // namespace trace_event
-}  // namespace base
-
-#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider.cc b/base/trace_event/process_memory_totals_dump_provider.cc
deleted file mode 100644
index 1713ebf0..0000000
--- a/base/trace_event/process_memory_totals_dump_provider.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/trace_event/process_memory_totals_dump_provider.h"
-
-#include <stddef.h>
-
-#include "base/process/process_metrics.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/process_memory_totals.h"
-#include "build/build_config.h"
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-#include <fcntl.h>
-
-#include "base/files/file_util.h"
-
-namespace {
-bool kernel_supports_rss_peak_reset = true;
-const char kClearPeakRssCommand[] = "5";
-}
-#endif
-
-namespace base {
-namespace trace_event {
-
-// static
-uint64_t ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
-
-// static
-ProcessMemoryTotalsDumpProvider*
-ProcessMemoryTotalsDumpProvider::GetInstance() {
-  return Singleton<
-      ProcessMemoryTotalsDumpProvider,
-      LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get();
-}
-
-ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider()
-    : process_metrics_(ProcessMetrics::CreateCurrentProcessMetrics()) {}
-
-ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() {
-}
-
-// Called at trace dump point time. Creates a snapshot the memory counters for
-// the current process.
-bool ProcessMemoryTotalsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
-                                                   ProcessMemoryDump* pmd) {
-  const uint64_t rss_bytes = rss_bytes_for_testing
-                                 ? rss_bytes_for_testing
-                                 : process_metrics_->GetWorkingSetSize();
-
-  uint64_t peak_rss_bytes = 0;
-
-#if !defined(OS_IOS)
-  peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-  if (kernel_supports_rss_peak_reset) {
-    // TODO(ssid): Fix crbug.com/461788 to write to the file from sandboxed
-    // processes.
-    int clear_refs_fd = open("/proc/self/clear_refs", O_WRONLY);
-    if (clear_refs_fd > 0 &&
-        WriteFileDescriptor(clear_refs_fd, kClearPeakRssCommand,
-                            sizeof(kClearPeakRssCommand))) {
-      pmd->process_totals()->set_is_peak_rss_resetable(true);
-    } else {
-      kernel_supports_rss_peak_reset = false;
-    }
-    close(clear_refs_fd);
-  }
-#elif defined(OS_MACOSX)
-  size_t private_bytes;
-  bool res = process_metrics_->GetMemoryBytes(&private_bytes,
-                                              nullptr /* shared_bytes */);
-  if (res) {
-    pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
-  }
-#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
-#endif  // !defined(OS_IOS)
-
-  if (rss_bytes > 0) {
-    pmd->process_totals()->set_resident_set_bytes(rss_bytes);
-    pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
-    pmd->set_has_process_totals();
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace trace_event
-}  // namespace base
diff --git a/base/trace_event/process_memory_totals_dump_provider.h b/base/trace_event/process_memory_totals_dump_provider.h
deleted file mode 100644
index d9573d31..0000000
--- a/base/trace_event/process_memory_totals_dump_provider.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
-#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
-
-#include <stdint.h>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/singleton.h"
-#include "base/trace_event/memory_dump_provider.h"
-
-namespace base {
-
-class ProcessMetrics;
-
-namespace trace_event {
-
-// Dump provider which collects process-wide memory stats.
-class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider {
- public:
-  static ProcessMemoryTotalsDumpProvider* GetInstance();
-
-  // MemoryDumpProvider implementation.
-  bool OnMemoryDump(const MemoryDumpArgs& args,
-                    ProcessMemoryDump* pmd) override;
-
- private:
-  friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
-  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS);
-
-  static uint64_t rss_bytes_for_testing;
-
-  ProcessMemoryTotalsDumpProvider();
-  ~ProcessMemoryTotalsDumpProvider() override;
-
-  scoped_ptr<ProcessMetrics> process_metrics_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider);
-};
-
-}  // namespace trace_event
-}  // namespace base
-
-#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
deleted file mode 100644
index d3f517e2..0000000
--- a/base/trace_event/process_memory_totals_dump_provider_unittest.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/trace_event/process_memory_totals_dump_provider.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/trace_event/process_memory_dump.h"
-#include "base/trace_event/process_memory_totals.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace trace_event {
-
-TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
-  const MemoryDumpArgs high_detail_args = {MemoryDumpLevelOfDetail::DETAILED};
-  auto pmtdp = ProcessMemoryTotalsDumpProvider::GetInstance();
-  scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump(nullptr));
-  scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump(nullptr));
-
-  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
-  pmtdp->OnMemoryDump(high_detail_args, pmd_before.get());
-
-  // Pretend that the RSS of the process increased of +1M.
-  const size_t kAllocSize = 1048576;
-  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize;
-
-  pmtdp->OnMemoryDump(high_detail_args, pmd_after.get());
-
-  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
-
-  ASSERT_TRUE(pmd_before->has_process_totals());
-  ASSERT_TRUE(pmd_after->has_process_totals());
-
-  const uint64_t rss_before =
-      pmd_before->process_totals()->resident_set_bytes();
-  const uint64_t rss_after = pmd_after->process_totals()->resident_set_bytes();
-
-  EXPECT_NE(0U, rss_before);
-  EXPECT_NE(0U, rss_after);
-
-  EXPECT_EQ(rss_after - rss_before, kAllocSize);
-}
-
-}  // namespace trace_event
-}  // namespace base
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
index 6948d7c..090f7da2c 100644
--- a/base/trace_event/trace_event.gypi
+++ b/base/trace_event/trace_event.gypi
@@ -36,11 +36,8 @@
       'trace_event/process_memory_dump.h',
       'trace_event/process_memory_maps.cc',
       'trace_event/process_memory_maps.h',
-      'trace_event/process_memory_maps_dump_provider.h',
       'trace_event/process_memory_totals.cc',
       'trace_event/process_memory_totals.h',
-      'trace_event/process_memory_totals_dump_provider.cc',
-      'trace_event/process_memory_totals_dump_provider.h',
       'trace_event/trace_buffer.cc',
       'trace_event/trace_buffer.h',
       'trace_event/trace_config.cc',
@@ -79,7 +76,6 @@
       'trace_event/memory_allocator_dump_unittest.cc',
       'trace_event/memory_dump_manager_unittest.cc',
       'trace_event/process_memory_dump_unittest.cc',
-      'trace_event/process_memory_totals_dump_provider_unittest.cc',
       'trace_event/trace_config_memory_test_util.h',
       'trace_event/trace_config_unittest.cc',
       'trace_event/trace_event_argument_unittest.cc',
@@ -95,14 +91,6 @@
           'trace_event/malloc_dump_provider.h',
         ],
       }],
-      ['OS == "linux" or OS == "android"', {
-          'trace_event_sources': [
-            'trace_event/process_memory_maps_dump_provider.cc',
-          ],
-          'trace_event_test_sources' : [
-            'trace_event/process_memory_maps_dump_provider_unittest.cc',
-          ],
-      }],
       ['OS == "android"', {
         'trace_event_test_sources' : [
           'trace_event/trace_event_android_unittest.cc',
diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp
index 69ce5be..7053d89 100644
--- a/breakpad/breakpad.gyp
+++ b/breakpad/breakpad.gyp
@@ -311,6 +311,7 @@
               'xcode_settings' : {
                 'WARNING_CFLAGS': [
                   # See https://bugs.chromium.org/p/google-breakpad/issues/detail?id=675.
+                  # TODO(crbug.com/569158): remove when fixed.
                   '-Wno-deprecated-declarations',
                 ],
               },
@@ -871,6 +872,13 @@
               'src',
             ],
           },
+          'xcode_settings' : {
+            'WARNING_CFLAGS': [
+              # See https://bugs.chromium.org/p/google-breakpad/issues/detail?id=675.
+              # TODO(crbug.com/569158): remove when fixed.
+              '-Wno-deprecated-declarations',
+            ],
+          },
         }
       ]
     }],
diff --git a/build/android/devil/utils/lsusb_test.py b/build/android/devil/utils/lsusb_test.py
index 7c80d6e..529a4b0 100755
--- a/build/android/devil/utils/lsusb_test.py
+++ b/build/android/devil/utils/lsusb_test.py
@@ -222,25 +222,25 @@
   def testLsusb(self):
     with self.assertCalls(
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-            ['lsusb'], timeout=2), (None, DEVICE_LIST)),
+            ['lsusb'], timeout=10), (None, DEVICE_LIST)),
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-          ['lsusb', '-v', '-s', '003:007'], timeout=2), (None, RAW_OUTPUT))):
+          ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
       self.assertDictEqual(lsusb.lsusb().pop(), EXPECTED_RESULT)
 
   def testGetSerial(self):
     with self.assertCalls(
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-            ['lsusb'], timeout=2), (None, DEVICE_LIST)),
+            ['lsusb'], timeout=10), (None, DEVICE_LIST)),
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-          ['lsusb', '-v', '-s', '003:007'], timeout=2), (None, RAW_OUTPUT))):
+          ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
       self.assertEqual(lsusb.get_android_devices(), ['01d2450ea194a93b'])
 
   def testGetLsusbSerial(self):
     with self.assertCalls(
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-            ['lsusb'], timeout=2), (None, DEVICE_LIST)),
+            ['lsusb'], timeout=10), (None, DEVICE_LIST)),
         (mock.call.devil.utils.cmd_helper.GetCmdStatusAndOutputWithTimeout(
-          ['lsusb', '-v', '-s', '003:007'], timeout=2), (None, RAW_OUTPUT))):
+          ['lsusb', '-v', '-s', '003:007'], timeout=10), (None, RAW_OUTPUT))):
       out = lsusb.lsusb().pop()
       self.assertEqual(lsusb.get_lsusb_serial(out), '01d2450ea194a93b')
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
index c0823d3..466b802 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerBridge.java
@@ -144,6 +144,8 @@
                 nativeOnCastStopping(mNativeRemoteMediaPlayerBridge);
             }
             mActive = false;
+            // Free the poster bitmap to save memory
+            mPosterBitmap = null;
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
index e8236b1..5593a0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -272,7 +272,7 @@
     @CalledByNative
     public void closeRoute(String routeId) {
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
-        assert provider != null;
+        if (provider == null) return;
 
         provider.closeRoute(routeId);
     }
@@ -284,7 +284,7 @@
     @CalledByNative
     public void detachRoute(String routeId) {
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
-        assert provider != null;
+        if (provider == null) return;
 
         provider.detachRoute(routeId);
         mRouteIdsToProviders.remove(routeId);
@@ -299,7 +299,10 @@
     @CalledByNative
     public void sendStringMessage(String routeId, String message, int callbackId) {
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
-        assert provider != null;
+        if (provider == null) {
+            nativeOnMessageSentResult(mNativeMediaRouterAndroid, false, callbackId);
+            return;
+        }
 
         provider.sendStringMessage(routeId, message, callbackId);
     }
@@ -313,7 +316,10 @@
     @CalledByNative
     public void sendBinaryMessage(String routeId, byte[] data, int callbackId) {
         MediaRouteProvider provider = mRouteIdsToProviders.get(routeId);
-        assert provider != null;
+        if (provider == null) {
+            nativeOnMessageSentResult(mNativeMediaRouterAndroid, false, callbackId);
+            return;
+        }
 
         provider.sendBinaryMessage(routeId, data, callbackId);
     }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index f588683a..6a564f3 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -62,6 +62,7 @@
     "//components/certificate_reporting:encrypted_cert_logger_proto",
     "//components/drive",
     "//components/drive:drive_chromeos",
+    "//components/feedback",
     "//components/flags_ui",
     "//components/login",
     "//components/onc",
diff --git a/chrome/browser/chromeos/policy/DEPS b/chrome/browser/chromeos/policy/DEPS
index 887ac0fd..a4f1c9827 100644
--- a/chrome/browser/chromeos/policy/DEPS
+++ b/chrome/browser/chromeos/policy/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/feedback",
   "+components/invalidation",
   "+components/user_manager",
 ]
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc
index bb68674..8664c53 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/policy/system_log_uploader.h"
+#include "system_log_uploader.h"
 
 #include <utility>
 
@@ -10,11 +10,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_runner_util.h"
 #include "chrome/browser/browser_process.h"
@@ -22,11 +18,10 @@
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
 #include "chrome/common/chrome_switches.h"
+#include "components/feedback/anonymizer_tool.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/core/common/cloud/enterprise_metrics.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/http/http_request_headers.h"
-#include "third_party/re2/src/re2/re2.h"
 
 namespace {
 // The maximum number of successive retries.
@@ -44,39 +39,12 @@
     "/var/log/net.log",       "/var/log/net.1.log",
     "/var/log/ui/ui.LATEST",  "/var/log/update_engine.log"};
 
-const char kEmailAddress[] =
-    "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}\\@"
-    "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25})+";
-const char kIPAddress[] =
-    "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])"
-    "\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2"
-    "[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
-    "[0-9]{2}|[1-9][0-9]|[0-9]))";
-const char kIPv6Address[] =
-    "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
-    "([0-9a-fA-F]{1,4}:){1,7}:|"
-    "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
-    "([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
-    "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
-    "([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
-    "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
-    "[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
-    ":((:[0-9a-fA-F]{1,4}){1,7}|:)|"
-    "fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
-    "::(ffff(:0{1,4}){0,1}:){0,1}"
-    "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}"
-    "(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|"
-    "([0-9a-fA-F]{1,4}:){1,4}:"
-    "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}"
-    "(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))";
-
-const char kWebUrl[] = "(http|https|Http|Https|rtsp|Rtsp):\\/\\/";
-
-// Reads the system log files as binary files, stores the files as pairs
-// (file name, data) and returns. Called on blocking thread.
+// Reads the system log files as binary files, anonymizes data, stores the files
+// as pairs (file name, data) and returns. Called on blocking thread.
 scoped_ptr<policy::SystemLogUploader::SystemLogs> ReadFiles() {
   scoped_ptr<policy::SystemLogUploader::SystemLogs> system_logs(
       new policy::SystemLogUploader::SystemLogs());
+  feedback::AnonymizerTool anonymizer;
   for (auto const file_path : kSystemLogFileNames) {
     if (!base::PathExists(base::FilePath(file_path)))
       continue;
@@ -86,7 +54,8 @@
                  << file_path << std::endl;
     }
     system_logs->push_back(std::make_pair(
-        file_path, policy::SystemLogUploader::RemoveSensitiveData(data)));
+        file_path,
+        policy::SystemLogUploader::RemoveSensitiveData(&anonymizer, data)));
   }
   return system_logs;
 }
@@ -155,11 +124,6 @@
   return upload_frequency;
 }
 
-void RecordSystemLogPIILeak(policy::SystemLogPIIType type) {
-  UMA_HISTOGRAM_ENUMERATION(policy::kMetricSystemLogPII, type,
-                            policy::SYSTEM_LOG_PII_TYPE_SIZE);
-}
-
 std::string GetUploadUrl() {
   return policy::BrowserPolicyConnector::GetDeviceManagementUrl() +
          kSystemLogUploadUrlTail;
@@ -248,48 +212,11 @@
 }
 
 // static
-std::string SystemLogUploader::RemoveSensitiveData(const std::string& data) {
-  std::string result = "";
-  RE2 email_pattern(kEmailAddress), ipv4_pattern(kIPAddress),
-      ipv6_pattern(kIPv6Address), url_pattern(kWebUrl);
-
-  for (const std::string& line : base::SplitString(
-           data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
-    // Email.
-    if (RE2::PartialMatch(line, email_pattern)) {
-      RecordSystemLogPIILeak(SYSTEM_LOG_PII_TYPE_EMAIL_ADDRESS);
-      continue;
-    }
-
-    // IPv4 address.
-    if (RE2::PartialMatch(line, ipv4_pattern)) {
-      RecordSystemLogPIILeak(SYSTEM_LOG_PII_TYPE_IP_ADDRESS);
-      continue;
-    }
-
-    // IPv6 address.
-    if (RE2::PartialMatch(line, ipv6_pattern)) {
-      RecordSystemLogPIILeak(SYSTEM_LOG_PII_TYPE_IP_ADDRESS);
-      continue;
-    }
-
-    // URL.
-    if (RE2::PartialMatch(line, url_pattern)) {
-      RecordSystemLogPIILeak(SYSTEM_LOG_PII_TYPE_WEB_URL);
-      continue;
-    }
-
-    // SSID.
-    if (line.find("SSID=") != std::string::npos) {
-      RecordSystemLogPIILeak(SYSTEM_LOG_PII_TYPE_SSID);
-      continue;
-    }
-
-    result += line + "\n";
-  }
-  return result;
+std::string SystemLogUploader::RemoveSensitiveData(
+    feedback::AnonymizerTool* const anonymizer,
+    const std::string& data) {
+  return anonymizer->Anonymize(data);
 }
-
 void SystemLogUploader::RefreshUploadSettings() {
   // Attempt to fetch the current value of the reporting settings.
   // If trusted values are not available, register this function to be called
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.h b/chrome/browser/chromeos/policy/system_log_uploader.h
index 5a3d624..2da5365 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.h
+++ b/chrome/browser/chromeos/policy/system_log_uploader.h
@@ -21,6 +21,10 @@
 class SequencedTaskRunner;
 }
 
+namespace feedback {
+class AnonymizerTool;
+}
+
 namespace policy {
 
 // Class responsible for periodically uploading system logs, it handles the
@@ -78,9 +82,11 @@
   void OnSuccess() override;
   void OnFailure(UploadJob::ErrorCode error_code) override;
 
-  // Remove lines from |data| that contain common PII (IP addresses, SSIDs, URLs
-  // e-mail addresses).
-  static std::string RemoveSensitiveData(const std::string& data);
+  // Remove lines from |data| that contain common PII (IP addresses, BSSIDs,
+  // SSIDs, URLs, e-mail addresses).
+  static std::string RemoveSensitiveData(
+      feedback::AnonymizerTool* const anonymizer,
+      const std::string& data);
 
  private:
   // Updates the system log upload enabled field from settings.
diff --git a/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc b/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc
index e30311c..d5bbe14 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
+#include "components/feedback/anonymizer_tool.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "net/http/http_request_headers.h"
@@ -288,23 +289,31 @@
 
 // Test RemovePII function.
 TEST_F(SystemLogUploaderTest, TestPII) {
+  feedback::AnonymizerTool anonymizer;
   std::string data =
-      "aaaaaaaaSSID=123aaaaaaaaaaa\n"     // SSID.
+      "aaaaaaaa [SSID=123aaaaaa]aaaaa\n"  // SSID.
       "aaaaaaaahttp://tets.comaaaaaaa\n"  // URL.
       "aaaaaemail@example.comaaa\n"       //  Email address.
-      "example@@1234\n"          //  No PII, it is not valid email address.
-      "255.255.355.255\n"        // No PII, it is not valid IP address format.
-      "aaaa123.123.45.4aaa\n"    // IP address.
-      "11:11;11::11\n"           // IP address.
-      "11::11\n"                 // IP address.
-      "11:11:abcdef:0:0:0:0:0";  // No PII, it is not valid IP address format.
+      "example@@1234\n"           //  No PII, it is not valid email address.
+      "255.255.155.255\n"         // IP address.
+      "aaaa123.123.45.4aaa\n"     // IP address.
+      "11:11;11::11\n"            // IP address.
+      "11::11\n"                  // IP address.
+      "11:11:abcdef:0:0:0:0:0\n"  // No PII.
+      "aa:aa:aa:aa:aa:aa";        // MAC address (BSSID).
 
   std::string result =
+      "aaaaaaaa [SSID=1]aaaaa\n"
+      "aaaaaaaa<URL: 1>\n"
+      "<email: 1>\n"
       "example@@1234\n"
-      "255.255.355.255\n"
-      "11:11:abcdef:0:0:0:0:0\n";
-
-  EXPECT_EQ(result, SystemLogUploader::RemoveSensitiveData(data));
+      "<IPv4: 1>55\n"
+      "aaaa<IPv4: 2>aaa\n"
+      "11:11;<IPv6: 1>\n"
+      "<IPv6: 1>\n"
+      "11:11:abcdef:0:0:0:0:0\n"
+      "aa:aa:aa:00:00:01";
+  EXPECT_EQ(result, SystemLogUploader::RemoveSensitiveData(&anonymizer, data));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index 4e0e1fcc..8d7913db 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -159,13 +159,7 @@
 
 }  // namespace
 
-// See crbug.com/576364 for details
-#if defined(OS_CHROMEOS)
-#define MAYBE_Basic DISABLED_Basic
-#else
-#define MAYBE_Basic Basic
-#endif
-IN_PROC_BROWSER_TEST_F(CertificateProviderApiTest, MAYBE_Basic) {
+IN_PROC_BROWSER_TEST_F(CertificateProviderApiTest, Basic) {
   // Start an HTTPS test server that requests a client certificate.
   net::SpawnedTestServer::SSLOptions ssl_options;
   ssl_options.request_client_certificate = true;
@@ -197,6 +191,9 @@
   content::WebContents* const https_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
 
+  VLOG(1) << "Wait for the extension to respond to the certificates request.";
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+
   VLOG(1) << "Wait for the extension to receive the sign request.";
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 
diff --git a/chrome/browser/extensions/api/management/management_browsertest.cc b/chrome/browser/extensions/api/management/management_browsertest.cc
index 86ff346..1034dda 100644
--- a/chrome/browser/extensions/api/management/management_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_browsertest.cc
@@ -624,8 +624,6 @@
       browser()->profile())->extension_service();
   ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
   const char kExtensionId[] = "ogjcoiohnmldgjemafoockdghcjciccf";
-  extensions::ExtensionUpdater::CheckParams params;
-  service->updater()->set_default_check_params(params);
   const size_t size_before = registry->enabled_extensions().size();
   base::FilePath basedir = test_data_dir_.AppendASCII("autoupdate");
   ASSERT_TRUE(registry->disabled_extensions().is_empty());
diff --git a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
index 47a791c..4466ade1 100644
--- a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
@@ -213,9 +213,6 @@
       GURL("http://localhost/autoupdate/v2.crx"),
       scoped_temp_dir_.path().AppendASCII("permissions2.crx"));
 
-  extensions::ExtensionUpdater::CheckParams params;
-  service_->updater()->set_default_check_params(params);
-
   extensions::TestExtensionRegistryObserver install_observer(registry_);
   sync_service->ProcessSyncChanges(
       FROM_HERE,
@@ -252,9 +249,6 @@
       GURL("http://localhost/autoupdate/v2.crx"),
       scoped_temp_dir_.path().AppendASCII("permissions2.crx"));
 
-  extensions::ExtensionUpdater::CheckParams params;
-  service_->updater()->set_default_check_params(params);
-
   sync_pb::EntitySpecifics specifics;
   specifics.mutable_extension()->set_id(extension_id);
   specifics.mutable_extension()->set_enabled(false);
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
index 939fe69..695e6a1 100644
--- a/chrome/browser/extensions/updater/extension_updater.cc
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -269,7 +269,7 @@
 
 void ExtensionUpdater::TimerFired() {
   DCHECK(alive_);
-  CheckNow(default_params_);
+  CheckNow(CheckParams());
 
   // If the user has overridden the update frequency, don't bother reporting
   // this.
@@ -321,7 +321,7 @@
 
 void ExtensionUpdater::DoCheckSoon() {
   DCHECK(will_check_soon_);
-  CheckNow(default_params_);
+  CheckNow(CheckParams());
   will_check_soon_ = false;
 }
 
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
index 3cb450d..772eef6 100644
--- a/chrome/browser/extensions/updater/extension_updater.h
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -119,12 +119,6 @@
   // code should just call CheckSoon().
   bool WillCheckSoon() const;
 
-  // Changes the params that are used for the automatic periodic update checks,
-  // as well as for explicit calls to CheckSoon.
-  void set_default_check_params(const CheckParams& params) {
-    default_params_ = params;
-  }
-
   // Overrides the extension cache with |extension_cache| for testing.
   void SetExtensionCacheForTesting(ExtensionCache* extension_cache);
 
@@ -271,8 +265,6 @@
   std::stack<FetchedCRXFile> fetched_crx_files_;
   FetchedCRXFile current_crx_file_;
 
-  CheckParams default_params_;
-
   ExtensionCache* extension_cache_;
 
   // Keeps track of when an extension tried to update itself, so we can throttle
diff --git a/chrome/browser/media/android/remote/remote_media_player_manager.cc b/chrome/browser/media/android/remote/remote_media_player_manager.cc
index 375e7e0..3513765 100644
--- a/chrome/browser/media/android/remote/remote_media_player_manager.cc
+++ b/chrome/browser/media/android/remote/remote_media_player_manager.cc
@@ -12,6 +12,8 @@
 
 using media::MediaPlayerAndroid;
 
+static const int MAX_POSTER_BITMAP_SIZE = 1024 * 1024;
+
 namespace remote_media {
 
 RemoteMediaPlayerManager::RemoteMediaPlayerManager(
@@ -50,6 +52,7 @@
   RemoteMediaPlayerBridge* player = GetRemotePlayer(player_id);
   if (player)
     player->OnPlayerDestroyed();
+  poster_urls_.erase(player_id);
   BrowserMediaPlayerManager::OnDestroyPlayer(player_id);
 }
 
@@ -86,27 +89,30 @@
   return tab->GetAndroidId();
 }
 
-void RemoteMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) {
+void RemoteMediaPlayerManager::FetchPosterBitmap(int player_id) {
   RemoteMediaPlayerBridge* player = GetRemotePlayer(player_id);
-
-  if (player && url.is_empty()) {
-    player->SetPosterBitmap(std::vector<SkBitmap>());
-  } else {
-    // TODO(aberent) OnSetPoster is called when the attributes of the video
-    // element are parsed, which may be before OnInitialize is called. We are
-    // here relying on the image fetch taking longer than the delay until
-    // OnInitialize is called, and hence the player is created. This is not
-    // guaranteed.
-    content::WebContents::ImageDownloadCallback callback = base::Bind(
-        &RemoteMediaPlayerManager::DidDownloadPoster,
-        weak_ptr_factory_.GetWeakPtr(), player_id);
-    web_contents()->DownloadImage(
-        url,
-        false,  // is_favicon, false so that cookies will be used.
-        0,  // max_bitmap_size, 0 means no limit.
-        false, // normal cache policy.
-        callback);
+  if (poster_urls_.count(player_id) == 0 ||
+      poster_urls_[player_id].is_empty()) {
+    if (player)
+      player->SetPosterBitmap(std::vector<SkBitmap>());
+    return;
   }
+  content::WebContents::ImageDownloadCallback callback =
+      base::Bind(&RemoteMediaPlayerManager::DidDownloadPoster,
+                 weak_ptr_factory_.GetWeakPtr(), player_id);
+  web_contents()->DownloadImage(
+      poster_urls_[player_id],
+      false,  // is_favicon, false so that cookies will be used.
+      MAX_POSTER_BITMAP_SIZE,  // max_bitmap_size, 0 means no limit.
+      false,                   // normal cache policy.
+      callback);
+}
+
+void RemoteMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) {
+  // OnSetPoster is called when the attibutes of the video element are parsed,
+  // which may be before OnInitialize is called, so we can't assume that the
+  // players wil exist.
+  poster_urls_[player_id] = url;
 }
 
 void RemoteMediaPlayerManager::ReleaseResources(int player_id) {
@@ -171,6 +177,9 @@
   Send(new MediaPlayerMsg_DidMediaPlayerPlay(RoutingID(), player_id));
   Send(new MediaPlayerMsg_ConnectedToRemoteDevice(RoutingID(), player_id,
                                                   casting_message));
+  // The remote player will want the poster bitmap, however, to avoid wasting
+  // memory we don't fetch it until we are likely to need it.
+  FetchPosterBitmap(player_id);
 }
 
 void RemoteMediaPlayerManager::SwitchToLocalPlayer(int player_id) {
diff --git a/chrome/browser/media/android/remote/remote_media_player_manager.h b/chrome/browser/media/android/remote/remote_media_player_manager.h
index e0cef391..fea7caa 100644
--- a/chrome/browser/media/android/remote/remote_media_player_manager.h
+++ b/chrome/browser/media/android/remote/remote_media_player_manager.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_MEDIA_ANDROID_REMOTE_REMOTE_MEDIA_PLAYER_MANAGER_H_
 
 #include <set>
+#include <unordered_map>
 #include <vector>
 
 #include "base/macros.h"
@@ -105,12 +106,15 @@
 
   void SwapCurrentPlayer(int player_id);
 
+  void FetchPosterBitmap(int player_id);
+
   // Contains the alternative players that are not currently in use, i.e. the
   // remote players for videos that are playing locally, and the local players
   // for videos that are playing remotely.
   ScopedVector<media::MediaPlayerAndroid> alternative_players_;
 
   std::set<int> players_playing_remotely_;
+  std::unordered_map<int, GURL> poster_urls_;
 
   base::WeakPtrFactory<RemoteMediaPlayerManager> weak_ptr_factory_;
 
diff --git a/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc b/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc
index 4821d4e..c073cf1 100644
--- a/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc
+++ b/chrome/browser/media/chrome_webrtc_apprtc_browsertest.cc
@@ -229,7 +229,9 @@
   base::Process collider_server_;
 };
 
-IN_PROC_BROWSER_TEST_F(WebRtcApprtcBrowserTest, MANUAL_WorksOnApprtc) {
+// Broken because of dependency updates: http://crbug.com/581283.
+IN_PROC_BROWSER_TEST_F(WebRtcApprtcBrowserTest,
+                       DISABLED_MANUAL_WorksOnApprtc) {
   DetectErrorsInJavaScript();
   ASSERT_TRUE(LaunchApprtcInstanceOnLocalhost("9999"));
   ASSERT_TRUE(LaunchColliderOnLocalHost("http://localhost:9999", "8089"));
@@ -269,10 +271,11 @@
   chrome::CloseWebContents(browser(), right_tab, false);
 }
 
-#if defined(OS_LINUX)
+#if 0
 #define MAYBE_MANUAL_FirefoxApprtcInteropTest MANUAL_FirefoxApprtcInteropTest
 #else
 // Not implemented yet on Windows and Mac.
+// Broken because of dependency updates: http://crbug.com/581283.
 #define MAYBE_MANUAL_FirefoxApprtcInteropTest DISABLED_MANUAL_FirefoxApprtcInteropTest
 #endif
 
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
index 4a5a310..800b2547 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
@@ -296,11 +296,6 @@
               Constants.CustomWallpaperThumbnailSuffix, '');
           WallpaperUtil.deleteWallpaperFromLocalFS(fileName);
         }
-      } else {  // detail.direction == 'local_to_remote'
-        if (detail.action == 'deleted') {
-          WallpaperUtil.deleteWallpaperFromSyncFS(detail.fileEntry.name);
-          WallpaperUtil.deleteWallpaperFromLocalFS(detail.fileEntry.name);
-        }
       }
     }
   });
diff --git a/chrome/browser/resources/ntp4/new_incognito_tab_theme.css b/chrome/browser/resources/ntp4/new_incognito_tab_theme.css
index 496b95b..62ec092 100644
--- a/chrome/browser/resources/ntp4/new_incognito_tab_theme.css
+++ b/chrome/browser/resources/ntp4/new_incognito_tab_theme.css
@@ -5,21 +5,21 @@
 
 html {
   background-attachment: fixed;
-  background-color: ${colorBackground};
-  background-position: ${backgroundBarDetached};
-  background-repeat: ${backgroundTiling};
+  background-color: $i18n{colorBackground};
+  background-position: $i18n{backgroundBarDetached};
+  background-repeat: $i18n{backgroundTiling};
   height: 100%;
   overflow: auto;
 }
 
 html[hascustombackground='true'] {
-  background-image: url(chrome://theme/IDR_THEME_NTP_BACKGROUND?${themeId});
+  background-image: url(chrome://theme/IDR_THEME_NTP_BACKGROUND?$i18n{themeId});
 }
 
 html[bookmarkbarattached='true'] {
-  background-position: ${backgroundBarAttached};
+  background-position: $i18n{backgroundBarAttached};
 }
 
 #attribution-img {
-  content: url(chrome://theme/IDR_THEME_NTP_ATTRIBUTION?${themeId});
+  content: url(chrome://theme/IDR_THEME_NTP_ATTRIBUTION?$i18n{themeId});
 }
diff --git a/chrome/browser/resources/ntp4/new_tab_theme.css b/chrome/browser/resources/ntp4/new_tab_theme.css
index 6474290..7725c97e 100644
--- a/chrome/browser/resources/ntp4/new_tab_theme.css
+++ b/chrome/browser/resources/ntp4/new_tab_theme.css
@@ -1,53 +1,53 @@
-/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
+/* Copyright 2012 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. */
 
 html {
   background-attachment: fixed;
-  background-color: ${colorBackground};
-  background-image: url(chrome://theme/IDR_THEME_NTP_BACKGROUND?${themeId});
-  background-position: ${backgroundBarDetached};
-  background-repeat: ${backgroundTiling};
+  background-color: $i18n{colorBackground};
+  background-image: url(chrome://theme/IDR_THEME_NTP_BACKGROUND?$i18n{themeId});
+  background-position: $i18n{backgroundBarDetached};
+  background-repeat: $i18n{backgroundTiling};
 }
 
 #attribution {
-  left: ${leftAlignAttribution};
-  right: ${rightAlignAttribution};
-  text-align: ${textAlignAttribution};
-  display: ${displayAttribution};
+  left: $i18n{leftAlignAttribution};
+  right: $i18n{rightAlignAttribution};
+  text-align: $i18n{textAlignAttribution};
+  display: $i18n{displayAttribution};
 }
 
 #attribution-img {
-  content: url(chrome://theme/IDR_THEME_NTP_ATTRIBUTION?${themeId});
+  content: url(chrome://theme/IDR_THEME_NTP_ATTRIBUTION?$i18n{themeId});
 }
 
 html[bookmarkbarattached='true'] {
-  background-position: ${backgroundBarAttached};
+  background-position: $i18n{backgroundBarAttached};
 }
 
 body {
-  color: ${colorTextRgba};
+  color: $i18n{colorTextRgba};
   height: 100%;
   overflow: auto;
 }
 
 #attribution,
 [is='action-link'] {
-  color: ${colorTextLight};
+  color: $i18n{colorTextLight};
 }
 
 [is='action-link']:active {
-  color: ${colorTextRgba};
+  color: $i18n{colorTextRgba};
 }
 
 .page-switcher {
-  color: rgba(${colorText}, 0.5);
+  color: rgba($i18n{colorText}, 0.5);
 }
 
 .page-switcher:hover,
 .page-switcher:focus,
 .page-switcher.drag-target {
-  background-color: rgba(${colorText}, 0.06);
+  background-color: rgba($i18n{colorText}, 0.06);
 }
 
 /* Only change the background to a gradient when a promo is showing. */
@@ -55,34 +55,34 @@
 .showing-login-area #page-switcher-end:focus,
 .showing-login-area #page-switcher-end.drag-target {
   background: linear-gradient(top,
-      rgba(${colorText}, 0) 0,
-      rgba(${colorText}, .01) 60px,
-      rgba(${colorText}, .06) 183px);
+      rgba($i18n{colorText}, 0) 0,
+      rgba($i18n{colorText}, .01) 60px,
+      rgba($i18n{colorText}, .06) 183px);
 }
 
 .tile-page-scrollbar {
-  background-color: ${colorTextLight};
+  background-color: $i18n{colorTextLight};
 }
 
 /* Footer *********************************************************************/
 
 #footer-border {
   background: linear-gradient(left,
-      rgba(${colorSectionBorder}, 0.2),
-      rgba(${colorSectionBorder}, 0.3) 20%,
-      rgba(${colorSectionBorder}, 0.3) 80%,
-      rgba(${colorSectionBorder}, 0.2));
+      rgba($i18n{colorSectionBorder}, 0.2),
+      rgba($i18n{colorSectionBorder}, 0.3) 20%,
+      rgba($i18n{colorSectionBorder}, 0.3) 80%,
+      rgba($i18n{colorSectionBorder}, 0.2));
 }
 
 .dot input:focus {
-  background-color: ${colorBackground};
+  background-color: $i18n{colorBackground};
 }
 
 .thumbnail-wrapper {
   /* This shows through at the (rounded) thumbnail's corners. */
-  background-color: ${colorSectionBorder};
+  background-color: $i18n{colorSectionBorder};
 }
 
 .filler .thumbnail {
-  border-color: ${colorBackground};
+  border-color: $i18n{colorBackground};
 }
diff --git a/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc b/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
index 4c712e6c..6ac4d092 100644
--- a/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
+++ b/chrome/browser/ssl/chrome_security_state_model_client_browser_tests.cc
@@ -368,6 +368,33 @@
       false /* expect cert status error */);
 }
 
+// Tests that the Content Security Policy block-all-mixed-content
+// directive stops mixed content from running.
+IN_PROC_BROWSER_TEST_F(ChromeSecurityStateModelClientTest,
+                       MixedContentStrictBlocking) {
+  ASSERT_TRUE(https_server_.Start());
+  SetUpMockCertVerifierForHttpsServer(0, net::OK);
+
+  // Navigate to an HTTPS page that tries to run mixed content in an
+  // iframe, with strict mixed content blocking.
+  std::string replacement_path;
+  net::HostPortPair host_port_pair =
+      net::HostPortPair::FromURL(https_server_.GetURL("/"));
+  host_port_pair.set_host("different-host.test");
+  host_resolver()->AddRule("different-host.test",
+                           https_server_.GetURL("/").host());
+  GetFilePathWithHostAndPortReplacement(
+      "/ssl/page_runs_insecure_content_in_iframe_with_strict_blocking.html",
+      host_port_pair, &replacement_path);
+  ui_test_utils::NavigateToURL(browser(),
+                               https_server_.GetURL(replacement_path));
+  CheckSecurityInfoForSecure(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      SecurityStateModel::SECURE, SecurityStateModel::NO_DEPRECATED_SHA1,
+      SecurityStateModel::NO_MIXED_CONTENT,
+      false /* expect cert status error */);
+}
+
 IN_PROC_BROWSER_TEST_F(ChromeSecurityStateModelClientTest, BrokenHTTPS) {
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h
index e373587a..839d7c4 100644
--- a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h
+++ b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h
@@ -7,12 +7,17 @@
 
 #import <Cocoa/Cocoa.h>
 
+class ManagePasswordsBubbleModel;
+
 // Handles user interaction with the content view.
 @protocol ManagePasswordsBubbleContentViewDelegate<NSObject>
 
 // The user performed an action that should dismiss the bubble.
 - (void)viewShouldDismiss;
 
+// Returns the model object.
+@property(nonatomic, readonly) ManagePasswordsBubbleModel* model;
+
 @end
 
 // Base class for a state of the password management bubble.
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.h b/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.h
index 104dab9..656a1c6 100644
--- a/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.h
+++ b/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.h
@@ -44,10 +44,8 @@
 
 // Helper delegate for testing the views of the password management bubble.
 @interface ContentViewDelegateMock
-    : NSObject<ManagePasswordsBubbleContentViewDelegate> {
- @private
-  BOOL _dismissed;
-}
+    : NSObject<ManagePasswordsBubbleContentViewDelegate>
+@property(nonatomic) ManagePasswordsBubbleModel* model;
 @property(readonly, nonatomic) BOOL dismissed;
 @end
 
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.mm b/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.mm
index 0155e53..cbbf146 100644
--- a/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.mm
+++ b/chrome/browser/ui/cocoa/passwords/base_passwords_controller_test.mm
@@ -110,6 +110,7 @@
 
 @implementation ContentViewDelegateMock
 
+@synthesize model = _model;
 @synthesize dismissed = _dismissed;
 
 - (void)viewShouldDismiss {
diff --git a/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller.mm b/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller.mm
index 5877df79..d6df328d 100644
--- a/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller.mm
@@ -52,6 +52,8 @@
 
 - (void)close {
   [currentController_ bubbleWillDisappear];
+  // The bubble is about to be closed. It destroys the model.
+  model_ = nil;
   [super close];
 }
 
@@ -60,13 +62,11 @@
   currentController_.reset();
   if (model_->state() == password_manager::ui::PENDING_PASSWORD_STATE) {
     currentController_.reset([[SavePendingPasswordViewController alloc]
-        initWithModel:model_
-             delegate:self]);
+        initWithDelegate:self]);
   } else if (model_->state() ==
              password_manager::ui::PENDING_PASSWORD_UPDATE_STATE) {
     currentController_.reset([[UpdatePendingPasswordViewController alloc]
-        initWithModel:model_
-             delegate:self]);
+        initWithDelegate:self]);
   } else if (model_->state() == password_manager::ui::CONFIRMATION_STATE) {
     currentController_.reset(
         [[ManagePasswordsBubbleConfirmationViewController alloc]
@@ -148,6 +148,10 @@
   [self close];
 }
 
+- (ManagePasswordsBubbleModel*)model {
+  return model_;
+}
+
 @end
 
 @implementation ManagePasswordsBubbleController (Testing)
diff --git a/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller_unittest.mm
index fcc927e..627f91f 100644
--- a/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/passwords/passwords_bubble_controller_unittest.mm
@@ -81,4 +81,11 @@
             [[controller() currentController] class]);
 }
 
+TEST_F(ManagePasswordsBubbleControllerTest, ClearModelOnClose) {
+  SetUpUpdatePendingState(false);
+  EXPECT_TRUE(controller().model);
+  [controller() close];
+  EXPECT_FALSE(controller().model);
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.h b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.h
index a5c0cd8..02e98f4 100644
--- a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.h
+++ b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.h
@@ -10,18 +10,14 @@
 #include "base/mac/scoped_nsobject.h"
 #import "chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h"
 
-class ManagePasswordsBubbleModel;
 @class PasswordsListViewController;
 
 // Base class for the views that offer to save/update the user's password.
 @interface PendingPasswordViewController
     : ManagePasswordsBubbleContentViewController<NSTextViewDelegate> {
  @private
-  ManagePasswordsBubbleModel* model_;  // weak
   base::scoped_nsobject<NSButton> closeButton_;
 }
-- (id)initWithModel:(ManagePasswordsBubbleModel*)model
-           delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
 
 // Creates a controller for showing username/password and returns its view.
 - (NSView*)createPasswordView;
diff --git a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm
index 588462a..97a838e 100644
--- a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm
@@ -22,18 +22,12 @@
 
 @implementation PendingPasswordViewController
 
-- (id)initWithModel:(ManagePasswordsBubbleModel*)model
-           delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
-  if (([super initWithDelegate:delegate])) {
-    model_ = model;
-  }
-  return self;
-}
-
 - (BOOL)textView:(NSTextView*)textView
     clickedOnLink:(id)link
           atIndex:(NSUInteger)charIndex {
-  model_->OnBrandLinkClicked();
+  ManagePasswordsBubbleModel* model = [self model];
+  if (model)
+    model->OnBrandLinkClicked();
   [delegate_ viewShouldDismiss];
   return YES;
 }
@@ -90,8 +84,9 @@
   [view addSubview:closeButton_];
 
   // Title.
+  ManagePasswordsBubbleModel* model = [self model];
   HyperlinkTextView* titleView = TitleLabelWithLink(
-      model_->title(), model_->title_brand_link_range(), self);
+      model->title(), model->title_brand_link_range(), self);
 
   // Force the text to wrap to fit in the bubble size.
   int titleRightPadding =
@@ -177,7 +172,7 @@
 }
 
 - (ManagePasswordsBubbleModel*)model {
-  return model_;
+  return [delegate_ model];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.h b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.h
index 04b2fe1..d596bab1 100644
--- a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.h
+++ b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.h
@@ -18,9 +18,7 @@
   base::scoped_nsobject<NSButton> neverButton_;
   base::scoped_nsobject<PasswordsListViewController> passwordItem_;
 }
-- (SavePendingPasswordViewController*)
-initWithModel:(ManagePasswordsBubbleModel*)model
-     delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
+
 - (NSView*)createPasswordView;
 - (NSArray*)createButtonsAndAddThemToView:(NSView*)view;
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.mm b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.mm
index 1b7ff8e4..537370c 100644
--- a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller.mm
@@ -16,25 +16,21 @@
 
 @implementation SavePendingPasswordViewController
 
-- (SavePendingPasswordViewController*)
-initWithModel:(ManagePasswordsBubbleModel*)model
-     delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
-  self = [super initWithModel:model
-                     delegate:delegate];
-  return self;
-}
-
 - (NSButton*)defaultButton {
   return saveButton_;
 }
 
 - (void)onSaveClicked:(id)sender {
-  self.model->OnSaveClicked();
+  ManagePasswordsBubbleModel* model = self.model;
+  if (model)
+    model->OnSaveClicked();
   [delegate_ viewShouldDismiss];
 }
 
 - (void)onNeverForThisSiteClicked:(id)sender {
-  self.model->OnNeverForThisSiteClicked();
+  ManagePasswordsBubbleModel* model = self.model;
+  if (model)
+    model->OnNeverForThisSiteClicked();
   [delegate_ viewShouldDismiss];
 }
 
diff --git a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller_unittest.mm b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller_unittest.mm
index d4cfef7..61a8f2d 100644
--- a/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/passwords/save_pending_password_view_controller_unittest.mm
@@ -32,10 +32,10 @@
 
   SavePendingPasswordViewController* controller() {
     if (!controller_) {
+      [delegate() setModel:GetModelAndCreateIfNull()];
       controller_.reset([[SavePendingPasswordViewController alloc]
-          initWithModel:GetModelAndCreateIfNull()
-               delegate:delegate()]);
-      [controller_ loadView];
+          initWithDelegate:delegate()]);
+      [controller_ view];
     }
     return controller_.get();
   }
@@ -86,4 +86,16 @@
   EXPECT_FALSE([controller() createPasswordView]);
 }
 
+TEST_F(SavePendingPasswordViewControllerTest, CloseBubbleAndHandleClick) {
+  // A user may press mouse down, some navigation closes the bubble, mouse up
+  // still sends the action.
+  SetUpSavePendingState(false);
+  EXPECT_CALL(*ui_controller(), SavePassword()).Times(0);
+  EXPECT_CALL(*ui_controller(), NeverSavePassword()).Times(0);
+  [controller() bubbleWillDisappear];
+  [delegate() setModel:nil];
+  [controller().neverButton performClick:nil];
+  [controller().saveButton performClick:nil];
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.h b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.h
index a1f4602..b6853d0 100644
--- a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.h
+++ b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.h
@@ -21,9 +21,7 @@
   base::scoped_nsobject<CredentialsSelectionView>
       passwordWithUsernameSelectionItem_;
 }
-- (UpdatePendingPasswordViewController*)
-initWithModel:(ManagePasswordsBubbleModel*)model
-     delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
+
 - (NSView*)createPasswordView;
 - (NSArray*)createButtonsAndAddThemToView:(NSView*)view;
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.mm b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.mm
index 4bf4d42..186e38a 100644
--- a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller.mm
@@ -17,30 +17,28 @@
 
 @implementation UpdatePendingPasswordViewController
 
-- (UpdatePendingPasswordViewController*)
-initWithModel:(ManagePasswordsBubbleModel*)model
-     delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
-  self = [super initWithModel:model delegate:delegate];
-  return self;
-}
-
 - (NSButton*)defaultButton {
   return updateButton_;
 }
 
 - (void)onUpdateClicked:(id)sender {
-  if (passwordWithUsernameSelectionItem_) {
-    // Multi account case.
-    self.model->OnUpdateClicked(
-        *[passwordWithUsernameSelectionItem_ getSelectedCredentials]);
-  } else {
-    self.model->OnUpdateClicked(self.model->pending_password());
+  ManagePasswordsBubbleModel* model = [self model];
+  if (model) {
+    if (passwordWithUsernameSelectionItem_) {
+      // Multi account case.
+      model->OnUpdateClicked(
+          *[passwordWithUsernameSelectionItem_ getSelectedCredentials]);
+    } else {
+      model->OnUpdateClicked(model->pending_password());
+    }
   }
   [delegate_ viewShouldDismiss];
 }
 
 - (void)onNopeClicked:(id)sender {
-  self.model->OnNopeUpdateClicked();
+  ManagePasswordsBubbleModel* model = [self model];
+  if (model)
+    model->OnNopeUpdateClicked();
   [delegate_ viewShouldDismiss];
 }
 
diff --git a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller_unittest.mm b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller_unittest.mm
index ac3df4c..73efa29 100644
--- a/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/passwords/update_pending_password_view_controller_unittest.mm
@@ -36,10 +36,10 @@
 
   UpdatePendingPasswordViewController* controller() {
     if (!controller_) {
+      [delegate() setModel:GetModelAndCreateIfNull()];
       controller_.reset([[UpdatePendingPasswordViewController alloc]
-          initWithModel:GetModelAndCreateIfNull()
-               delegate:delegate()]);
-      [controller_ loadView];
+          initWithDelegate:delegate()]);
+      [controller_ view];
     }
     return controller_.get();
   }
@@ -92,4 +92,16 @@
             [[controller() createPasswordView] class]);
 }
 
+TEST_F(UpdatePendingPasswordViewControllerTest, CloseBubbleAndHandleClick) {
+  // A user may press mouse down, some navigation closes the bubble, mouse up
+  // still sends the action.
+  SetUpUpdatePendingState(false);
+  EXPECT_CALL(*ui_controller(), UpdatePassword(_)).Times(0);
+  EXPECT_CALL(*ui_controller(), OnNopeUpdateClicked()).Times(0);
+  [controller() bubbleWillDisappear];
+  [delegate() setModel:nil];
+  [controller().updateButton performClick:nil];
+  [controller().noButton performClick:nil];
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.cc b/chrome/browser/ui/passwords/manage_passwords_state.cc
index 8f2cfcc3..6d4508c 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state.cc
@@ -152,10 +152,15 @@
 void ManagePasswordsState::OnPasswordAutofilled(
     const PasswordFormMap& password_form_map,
     const GURL& origin) {
-  // TODO(vabr): Revert back to DCHECK once http://crbug.com/486931 is fixed.
-  CHECK(!password_form_map.empty());
+  DCHECK(!password_form_map.empty());
   ClearData();
-  if (password_form_map.begin()->second->is_public_suffix_match) {
+  bool only_PSL_matches =
+      find_if(password_form_map.begin(), password_form_map.end(),
+              [](const std::pair<const base::string16,
+                                 scoped_ptr<autofill::PasswordForm>>& p) {
+                return !p.second->is_public_suffix_match;
+              }) == password_form_map.end();
+  if (only_PSL_matches) {
     // Don't show the UI for PSL matched passwords. They are not stored for this
     // page and cannot be deleted.
     origin_ = GURL();
diff --git a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
index 972fb38..1c8b56d4 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
@@ -364,6 +364,31 @@
   TestAllUpdates();
 }
 
+TEST_F(ManagePasswordsStateTest, ActiveOnMixedPSLAndNonPSLMatched) {
+  autofill::PasswordFormMap password_form_map;
+  password_form_map.insert(std::make_pair(
+      test_local_form().username_value,
+      make_scoped_ptr(new autofill::PasswordForm(test_local_form()))));
+  autofill::PasswordForm psl_matched_test_form = test_local_form();
+  psl_matched_test_form.is_public_suffix_match = true;
+  password_form_map.insert(std::make_pair(
+      psl_matched_test_form.username_value,
+      make_scoped_ptr(new autofill::PasswordForm(psl_matched_test_form))));
+  GURL origin("https://example.com");
+  passwords_data().OnPasswordAutofilled(password_form_map, origin);
+
+  EXPECT_THAT(passwords_data().GetCurrentForms(),
+              ElementsAre(Pointee(test_local_form())));
+  EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
+  EXPECT_EQ(origin, passwords_data().origin());
+
+  // |passwords_data| should hold a separate copy of test_local_form().
+  EXPECT_THAT(passwords_data().GetCurrentForms(),
+              Not(Contains(&test_local_form())));
+  TestAllUpdates();
+}
+
 TEST_F(ManagePasswordsStateTest, InactiveOnPSLMatched) {
   autofill::PasswordForm psl_matched_test_form = test_local_form();
   psl_matched_test_form.is_public_suffix_match = true;
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.cc b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
index cd35ea5..b509ea2f 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
@@ -267,8 +267,9 @@
     : model_(manage_passwords_bubble_model) {
   int fixed_height = PasswordFormRow::GetFixedHeight(model_->state());
   for (const autofill::PasswordForm* password_form : password_forms) {
-    password_forms_rows_.push_back(
-        new PasswordFormRow(this, password_form, fixed_height));
+    if (!password_form->is_public_suffix_match)
+      password_forms_rows_.push_back(
+          new PasswordFormRow(this, password_form, fixed_height));
   }
   AddRows();
 }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index 610bbc4..cd023867 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -427,7 +427,15 @@
   // If we have a list of passwords to store for the current site, display
   // them to the user for management. Otherwise, render a "No passwords for
   // this site" message.
-  if (!parent_->model()->local_credentials().empty()) {
+
+  bool only_PSL_matches =
+      find_if(parent_->model()->local_credentials().begin(),
+              parent_->model()->local_credentials().end(),
+              [](const autofill::PasswordForm* form) {
+                return !form->is_public_suffix_match;
+              }) == parent_->model()->local_credentials().end();
+
+  if (!only_PSL_matches) {
     ManagePasswordItemsView* item = new ManagePasswordItemsView(
         parent_->model(), parent_->model()->local_credentials().get());
     layout->StartRowWithPadding(0, SINGLE_VIEW_COLUMN_SET, 0,
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index df78320..e74b0fc 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 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.
 
@@ -553,7 +553,7 @@
           : SkColorSetRGB(0x32, 0x32, 0x32);
 
   // Generate the replacements.
-  std::map<base::StringPiece, std::string> substitutions;
+  ui::TemplateReplacements substitutions;
 
   // Cache-buster for background.
   substitutions["themeId"] =
@@ -605,7 +605,7 @@
                      SkColorGetB(color_header));
 
   // Generate the replacements.
-  std::map<base::StringPiece, std::string> substitutions;
+  ui::TemplateReplacements substitutions;
 
   // Cache-buster for background.
   substitutions["themeId"] =
diff --git a/chrome/browser/web_dev_style/css_checker.py b/chrome/browser/web_dev_style/css_checker.py
index 3713561e..7d03a60 100644
--- a/chrome/browser/web_dev_style/css_checker.py
+++ b/chrome/browser/web_dev_style/css_checker.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Copyright 2012 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.
 
@@ -52,7 +52,7 @@
       return re.sub(re.compile(r'--[\d\w-]+: {.*?};', re.DOTALL), '', s)
 
     def _remove_template_expressions(s):
-      return re.sub(re.compile(r'\${[^}]*}', re.DOTALL), '', s)
+      return re.sub(re.compile(r'\$i18n{[^}]*}', re.DOTALL), '', s)
 
     def _remove_grit(s):
       grit_reg = re.compile(r"""
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 260b68b4..7720c68 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -1130,6 +1130,7 @@
         '../components/components.gyp:cloud_policy_proto',
         '../components/components.gyp:drive',
         '../components/components.gyp:drive_chromeos',
+        '../components/components.gyp:feedback_component',
         '../components/components.gyp:flags_ui',
         '../components/components.gyp:login',
         '../components/components.gyp:onc_component',
diff --git a/chrome/installer/test/alternate_version_generator.cc b/chrome/installer/test/alternate_version_generator.cc
index c0ec333..211b1e5 100644
--- a/chrome/installer/test/alternate_version_generator.cc
+++ b/chrome/installer/test/alternate_version_generator.cc
@@ -425,8 +425,8 @@
     return false;
   }
   DCHECK(modified);
-  return base::WriteFile(manifest, &contents[0], contents.size()) ==
-         contents.size();
+  int written = base::WriteFile(manifest, &contents[0], contents.size());
+  return written != -1 && static_cast<size_t>(written) == contents.size();
 }
 
 bool IncrementNewVersion(upgrade_test::Direction direction,
diff --git a/chrome/installer/upgrade_test.gyp b/chrome/installer/upgrade_test.gyp
index 07b283e..1a05535 100644
--- a/chrome/installer/upgrade_test.gyp
+++ b/chrome/installer/upgrade_test.gyp
@@ -4,6 +4,7 @@
 
 {
   'variables': {
+    'chromium_code': 1,
     'branding_dir': '../app/theme/<(branding_path_component)',
     'version_py': '<(DEPTH)/build/util/version.py',
     'version_path': '../../chrome/VERSION',
diff --git a/chrome/renderer/resources/OWNERS b/chrome/renderer/resources/OWNERS
new file mode 100644
index 0000000..cb68f46
--- /dev/null
+++ b/chrome/renderer/resources/OWNERS
@@ -0,0 +1,8 @@
+bauerb@chromium.org
+dbeam@chromium.org
+estade@chromium.org
+nkostylev@chromium.org
+pam@chromium.org
+xiyuan@chromium.org
+
+per-file offline.js=edwardjung@chromium.org
diff --git a/chrome/renderer/resources/default_100_percent/offline/100-offline-sprite.png b/chrome/renderer/resources/default_100_percent/offline/100-offline-sprite.png
index 068d4a1..06348354 100644
--- a/chrome/renderer/resources/default_100_percent/offline/100-offline-sprite.png
+++ b/chrome/renderer/resources/default_100_percent/offline/100-offline-sprite.png
Binary files differ
diff --git a/chrome/renderer/resources/default_200_percent/offline/200-offline-sprite.png b/chrome/renderer/resources/default_200_percent/offline/200-offline-sprite.png
index f321c354..3a1e92e 100644
--- a/chrome/renderer/resources/default_200_percent/offline/200-offline-sprite.png
+++ b/chrome/renderer/resources/default_200_percent/offline/200-offline-sprite.png
Binary files differ
diff --git a/chrome/renderer/resources/neterror.css b/chrome/renderer/resources/neterror.css
index 246c8a9..4ebd6c4 100644
--- a/chrome/renderer/resources/neterror.css
+++ b/chrome/renderer/resources/neterror.css
@@ -334,6 +334,17 @@
 }
 
 /* Offline page */
+.offline {
+  transition: -webkit-filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1),
+              background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1);
+  will-change: -webkit-filter, background-color;
+}
+
+.offline.inverted {
+  -webkit-filter: invert(100%);
+  background-color: #000;
+}
+
 .offline .interstitial-wrapper {
   color: #2b2b2b;
   font-size: 1em;
diff --git a/chrome/renderer/resources/offline.js b/chrome/renderer/resources/offline.js
index e16dfa6..21bd286 100644
--- a/chrome/renderer/resources/offline.js
+++ b/chrome/renderer/resources/offline.js
@@ -47,7 +47,8 @@
   this.activated = false;
   this.crashed = false;
   this.paused = false;
-
+  this.inverted = false;
+  this.invertTimer = 0;
   this.resizeTimerId_ = null;
 
   this.playCount = 0;
@@ -111,6 +112,8 @@
   GAP_COEFFICIENT: 0.6,
   GRAVITY: 0.6,
   INITIAL_JUMP_VELOCITY: 12,
+  INVERT_FADE_DURATION: 12000,
+  INVERT_DISTANCE: 700,
   MAX_CLOUDS: 6,
   MAX_OBSTACLE_LENGTH: 3,
   MAX_OBSTACLE_DUPLICATION: 2,
@@ -142,6 +145,7 @@
   CONTAINER: 'runner-container',
   CRASHED: 'crashed',
   ICON: 'icon-offline',
+  INVERTED: 'inverted',
   SNACKBAR: 'snackbar',
   SNACKBAR_SHOW: 'snackbar-show',
   TOUCH_CONTROLLER: 'controller'
@@ -158,20 +162,24 @@
     CACTUS_SMALL: {x: 228, y: 2},
     CLOUD: {x: 86, y: 2},
     HORIZON: {x: 2, y: 54},
+    MOON: {x: 484, y: 2},
     PTERODACTYL: {x: 134, y: 2},
     RESTART: {x: 2, y: 2},
-    TEXT_SPRITE: {x: 484, y: 2},
-    TREX: {x: 677, y: 2}
+    TEXT_SPRITE: {x: 655, y: 2},
+    TREX: {x: 848, y: 2},
+    STAR: {x: 645, y: 2}
   },
   HDPI: {
-    CACTUS_LARGE: {x: 652,y: 2},
-    CACTUS_SMALL: {x: 446,y: 2},
-    CLOUD: {x: 166,y: 2},
-    HORIZON: {x: 2,y: 104},
-    PTERODACTYL: {x: 260,y: 2},
-    RESTART: {x: 2,y: 2},
-    TEXT_SPRITE: {x: 954,y: 2},
-    TREX: {x: 1338,y: 2}
+    CACTUS_LARGE: {x: 652, y: 2},
+    CACTUS_SMALL: {x: 446, y: 2},
+    CLOUD: {x: 166, y: 2},
+    HORIZON: {x: 2, y: 104},
+    MOON: {x: 954, y: 2},
+    PTERODACTYL: {x: 260, y: 2},
+    RESTART: {x: 2, y: 2},
+    TEXT_SPRITE: {x: 1294, y: 2},
+    TREX: {x: 1678, y: 2},
+    STAR: {x: 1276, y: 2}
   }
 };
 
@@ -525,7 +533,8 @@
         this.horizon.update(0, this.currentSpeed, hasObstacles);
       } else {
         deltaTime = !this.started ? 0 : deltaTime;
-        this.horizon.update(deltaTime, this.currentSpeed, hasObstacles);
+        this.horizon.update(deltaTime, this.currentSpeed, hasObstacles,
+            this.inverted);
       }
 
       // Check for collisions.
@@ -542,12 +551,34 @@
         this.gameOver();
       }
 
-      var playAcheivementSound = this.distanceMeter.update(deltaTime,
+      var playAchievementSound = this.distanceMeter.update(deltaTime,
           Math.ceil(this.distanceRan));
 
-      if (playAcheivementSound) {
+      if (playAchievementSound) {
         this.playSound(this.soundFx.SCORE);
       }
+
+      // Night mode.
+      if (this.invertTimer > this.config.INVERT_FADE_DURATION) {
+        this.invertTimer = 0;
+        this.invertTrigger = false;
+        this.invert();
+      } else if (this.invertTimer) {
+        this.invertTimer += deltaTime;
+      } else {
+        var actualDistance =
+            this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan));
+
+        if (actualDistance > 0) {
+          this.invertTrigger = !(actualDistance %
+              this.config.INVERT_DISTANCE);
+
+          if (this.invertTrigger && this.invertTimer === 0) {
+            this.invertTimer += deltaTime;
+            this.invert();
+          }
+        }
+      }
     }
 
     if (!this.crashed) {
@@ -774,7 +805,6 @@
       this.crashed = false;
       this.distanceRan = 0;
       this.setSpeed(this.config.SPEED);
-
       this.time = getTimeStamp();
       this.containerEl.classList.remove(Runner.classes.CRASHED);
       this.clearCanvas();
@@ -782,7 +812,7 @@
       this.horizon.reset();
       this.tRex.reset();
       this.playSound(this.soundFx.BUTTON_PRESS);
-
+      this.invert(true);
       this.update();
     }
   },
@@ -791,7 +821,8 @@
    * Pause the game if the tab is not in focus.
    */
   onVisibilityChange: function(e) {
-    if (document.hidden || document.webkitHidden || e.type == 'blur') {
+    if (document.hidden || document.webkitHidden || e.type == 'blur' ||
+      document.visibilityState != 'visible') {
       this.stop();
     } else if (!this.crashed) {
       this.tRex.reset();
@@ -810,6 +841,21 @@
       sourceNode.connect(this.audioContext.destination);
       sourceNode.start(0);
     }
+  },
+
+  /**
+   * Inverts the current page / canvas colors.
+   * @param {boolean} Whether to reset colors.
+   */
+  invert: function(reset) {
+    if (reset) {
+      document.body.classList.toggle(Runner.classes.INVERTED, false);
+      this.invertTimer = 0;
+      this.inverted = false;
+    } else {
+      this.inverted = document.body.classList.toggle(Runner.classes.INVERTED,
+          this.invertTrigger);
+    }
   }
 };
 
@@ -1173,9 +1219,10 @@
  * @param {Object} dimensions
  * @param {number} gapCoefficient Mutipler in determining the gap.
  * @param {number} speed
+ * @param {number} opt_xOffset
  */
 function Obstacle(canvasCtx, type, spriteImgPos, dimensions,
-    gapCoefficient, speed) {
+    gapCoefficient, speed, opt_xOffset) {
 
   this.canvasCtx = canvasCtx;
   this.spritePos = spriteImgPos;
@@ -1184,7 +1231,7 @@
   this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH);
   this.dimensions = dimensions;
   this.remove = false;
-  this.xPos = 0;
+  this.xPos = dimensions.WIDTH + (opt_xOffset || 0);
   this.yPos = 0;
   this.width = 0;
   this.collisionBoxes = [];
@@ -1225,7 +1272,6 @@
     }
 
     this.width = this.typeConfig.width * this.size;
-    this.xPos = this.dimensions.WIDTH - this.width;
 
     // Check if obstacle can be positioned at various heights.
     if (Array.isArray(this.typeConfig.yPos))  {
@@ -1806,6 +1852,7 @@
   this.defaultString = '';
   this.flashTimer = 0;
   this.flashIterations = 0;
+  this.invertTrigger = false;
 
   this.config = DistanceMeter.config;
   this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS;
@@ -1949,7 +1996,6 @@
 
     if (!this.acheivement) {
       distance = this.getActualDistance(distance);
-
       // Score has gone beyond the initial digit count.
       if (distance > this.maxScore && this.maxScoreUnits ==
         this.config.MAX_DISTANCE_UNITS) {
@@ -2002,7 +2048,6 @@
     }
 
     this.drawHighScore();
-
     return playSound;
   },
 
@@ -2140,6 +2185,162 @@
 //******************************************************************************
 
 /**
+ * Nightmode shows a moon and stars on the horizon.
+ */
+function NightMode(canvas, spritePos, containerWidth) {
+  this.spritePos = spritePos;
+  this.canvas = canvas;
+  this.canvasCtx = canvas.getContext('2d');
+  this.xPos = containerWidth - 50;
+  this.yPos = 30;
+  this.currentPhase = 0;
+  this.opacity = 0;
+  this.containerWidth = containerWidth;
+  this.stars = [];
+  this.drawStars = false;
+  this.placeStars();
+};
+
+/**
+ * @enum {number}
+ */
+NightMode.config = {
+  FADE_SPEED: 0.035,
+  HEIGHT: 40,
+  MOON_SPEED: 0.25,
+  NUM_STARS: 2,
+  STAR_SIZE: 9,
+  STAR_SPEED: 0.3,
+  STAR_MAX_Y: 70,
+  WIDTH: 20
+};
+
+NightMode.phases = [140, 120, 100, 60, 40, 20, 0];
+
+NightMode.prototype = {
+  /**
+   * Update moving moon, changing phases.
+   * @param {boolean} activated Whether night mode is activated.
+   * @param {number} delta
+   */
+  update: function(activated, delta) {
+    // Moon phase.
+    if (activated && this.opacity == 0) {
+      this.currentPhase++;
+
+      if (this.currentPhase >= NightMode.phases.length) {
+        this.currentPhase = 0;
+      }
+    }
+
+    // Fade in / out.
+    if (activated && (this.opacity < 1 || this.opacity == 0)) {
+      this.opacity += NightMode.config.FADE_SPEED;
+    } else if (this.opacity > 0) {
+      this.opacity -= NightMode.config.FADE_SPEED;
+    }
+
+    // Set moon positioning.
+    if (this.opacity > 0) {
+      this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED);
+
+      // Update stars.
+      if (this.drawStars) {
+         for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
+            this.stars[i].x = this.updateXPos(this.stars[i].x,
+                NightMode.config.STAR_SPEED);
+         }
+      }
+      this.draw();
+    } else {
+      this.opacity = 0;
+      this.placeStars();
+    }
+    this.drawStars = true;
+  },
+
+  updateXPos: function(currentPos, speed) {
+    if (currentPos < -NightMode.config.WIDTH) {
+      currentPos = this.containerWidth;
+    } else {
+      currentPos -= speed;
+    }
+    return currentPos;
+  },
+
+  draw: function() {
+    var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 :
+         NightMode.config.WIDTH;
+    var moonSourceHeight = NightMode.config.HEIGHT;
+    var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase];
+    var moonOutputWidth = moonSourceWidth;
+    var starSize = NightMode.config.STAR_SIZE;
+    var starSourceX = Runner.spriteDefinition.LDPI.STAR.x;
+
+    if (IS_HIDPI) {
+      moonSourceWidth *= 2;
+      moonSourceHeight *= 2;
+      moonSourceX = this.spritePos.x +
+          (NightMode.phases[this.currentPhase] * 2);
+      starSize *= 2;
+      starSourceX = Runner.spriteDefinition.HDPI.STAR.x;
+    }
+
+    this.canvasCtx.save();
+    this.canvasCtx.globalAlpha = this.opacity;
+
+    // Stars.
+    if (this.drawStars) {
+      for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
+        this.canvasCtx.drawImage(Runner.imageSprite,
+            starSourceX, this.stars[i].sourceY, starSize, starSize,
+            Math.round(this.stars[i].x), this.stars[i].y,
+            NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE);
+      }
+    }
+
+    // Moon.
+    this.canvasCtx.drawImage(Runner.imageSprite, moonSourceX,
+        this.spritePos.y, moonSourceWidth, moonSourceHeight,
+        Math.round(this.xPos), this.yPos,
+        moonOutputWidth, NightMode.config.HEIGHT);
+
+    this.canvasCtx.globalAlpha = 1;
+    this.canvasCtx.restore();
+  },
+
+  // Do star placement.
+  placeStars: function() {
+    var segmentSize = Math.round(this.containerWidth /
+        NightMode.config.NUM_STARS);
+
+    for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
+      this.stars[i] = {};
+      this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1));
+      this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y);
+
+      if (IS_HIDPI) {
+        this.stars[i].sourceY = Runner.spriteDefinition.HDPI.STAR.y +
+            NightMode.config.STAR_SIZE * 2 * i;
+      } else {
+        this.stars[i].sourceY = Runner.spriteDefinition.LDPI.STAR.y +
+            NightMode.config.STAR_SIZE * i;
+      }
+    }
+  },
+
+  reset: function() {
+    this.currentPhase = 0;
+    this.opacity = 0;
+    this.update(false);
+  }
+
+};
+
+
+//******************************************************************************
+
+/**
  * Horizon Line.
  * Consists of two connecting lines. Randomly assigns a flat / bumpy horizon.
  * @param {HTMLCanvasElement} canvas
@@ -2287,6 +2488,7 @@
   this.horizonOffsets = [0, 0];
   this.cloudFrequency = this.config.CLOUD_FREQUENCY;
   this.spritePos = spritePos;
+  this.nightMode = null;
 
   // Cloud
   this.clouds = [];
@@ -2294,7 +2496,6 @@
 
   // Horizon
   this.horizonLine = null;
-
   this.init();
 };
 
@@ -2319,6 +2520,8 @@
   init: function() {
     this.addCloud();
     this.horizonLine = new HorizonLine(this.canvas, this.spritePos.HORIZON);
+    this.nightMode = new NightMode(this.canvas, this.spritePos.MOON,
+        this.dimensions.WIDTH);
   },
 
   /**
@@ -2327,10 +2530,12 @@
    * @param {boolean} updateObstacles Used as an override to prevent
    *     the obstacles from being updated / added. This happens in the
    *     ease in section.
+   * @param {boolean} showNightMode Night mode activated.
    */
-  update: function(deltaTime, currentSpeed, updateObstacles) {
+  update: function(deltaTime, currentSpeed, updateObstacles, showNightMode) {
     this.runningTime += deltaTime;
     this.horizonLine.update(deltaTime, currentSpeed);
+    this.nightMode.update(showNightMode);
     this.updateClouds(deltaTime, currentSpeed);
 
     if (updateObstacles) {
@@ -2365,6 +2570,8 @@
       this.clouds = this.clouds.filter(function(obj) {
         return !obj.remove;
       });
+    } else {
+      this.addCloud();
     }
   },
 
@@ -2404,6 +2611,10 @@
     }
   },
 
+  removeFirstObstacle: function() {
+    this.obstacles.shift();
+  },
+
   /**
    * Add a new obstacle.
    * @param {number} currentSpeed
@@ -2422,7 +2633,7 @@
 
       this.obstacles.push(new Obstacle(this.canvasCtx, obstacleType,
           obstacleSpritePos, this.dimensions,
-          this.gapCoefficient, currentSpeed));
+          this.gapCoefficient, currentSpeed, obstacleType.width));
 
       this.obstacleHistory.unshift(obstacleType.type);
 
@@ -2454,6 +2665,7 @@
   reset: function() {
     this.obstacles = [];
     this.horizonLine.reset();
+    this.nightMode.reset();
   },
 
   /**
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc
index 46483ff..3ed91b4 100644
--- a/chrome/service/service_utility_process_host.cc
+++ b/chrome/service/service_utility_process_host.cc
@@ -67,9 +67,10 @@
   ServiceSandboxedProcessLauncherDelegate() {}
 
   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override {
-    // Service process may run as windows service and it fails to create a
-    // window station.
-    return policy->SetAlternateDesktop(false) == sandbox::SBOX_ALL_OK;
+    // Ignore result of SetAlternateDesktop. Service process may run as windows
+    // service and it fails to create a window station.
+    base::IgnoreResult(policy->SetAlternateDesktop(false));
+    return true;
   }
 
  private:
diff --git a/chrome/test/base/tracing_browsertest.cc b/chrome/test/base/tracing_browsertest.cc
index 1b953594..69956069 100644
--- a/chrome/test/base/tracing_browsertest.cc
+++ b/chrome/test/base/tracing_browsertest.cc
@@ -61,12 +61,22 @@
                                       MemoryDumpManager::kTraceCategory,
                                       event_name, 10));
 
-    GURL url2("chrome://credits/");
+    // Create and destroy renderers while tracing is enabled.
+    GURL url2("chrome://credits");
     ui_test_utils::NavigateToURLWithDisposition(
         browser(), url2, NEW_FOREGROUND_TAB,
         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     ASSERT_NO_FATAL_FAILURE(ExecuteJavascriptOnCurrentTab());
 
+    // Close the current tab.
+    browser()->tab_strip_model()->CloseSelectedTabs();
+
+    GURL url3("chrome://settings");
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), url3, CURRENT_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+    ASSERT_NO_FATAL_FAILURE(ExecuteJavascriptOnCurrentTab());
+
     EXPECT_TRUE(WaitForWatchEvent(no_timeout));
     ASSERT_TRUE(EndTracing(&json_events));
 
diff --git a/chrome/test/data/ssl/page_runs_insecure_content_in_iframe_with_strict_blocking.html b/chrome/test/data/ssl/page_runs_insecure_content_in_iframe_with_strict_blocking.html
new file mode 100644
index 0000000..a5e8b3b
--- /dev/null
+++ b/chrome/test/data/ssl/page_runs_insecure_content_in_iframe_with_strict_blocking.html
@@ -0,0 +1,10 @@
+<html>
+<head><title>Page that runs insecure content in an iframe with strict blocking</title></head>
+<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
+<body>
+This page contains an iframe which loads contents over an http connection,
+which would cause insecure content when this page is loaded over https, but
+mixed content is strictly blocked with CSP.<br>
+<iframe src="https://REPLACE_WITH_HOST_AND_PORT/ssl/iframe_with_insecure_content.html"/>
+</body>
+</html>
diff --git a/chrome/test/remoting/webapp_javascript_unittest.cc b/chrome/test/remoting/webapp_javascript_unittest.cc
index 017a139..676daa9 100644
--- a/chrome/test/remoting/webapp_javascript_unittest.cc
+++ b/chrome/test/remoting/webapp_javascript_unittest.cc
@@ -12,8 +12,9 @@
 
 namespace remoting {
 
-// Flakily times out on Win7 Tests (dbg): https://crbug.com/504204.
-#if defined(OS_WIN) && !defined(NDEBUG)
+// Flakily times out on Win7 Tests (dbg) and Linux Tests (dbg)(1):
+// https://crbug.com/504204.
+#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(NDEBUG)
 #define MAYBE_Remoting_Webapp_Js_Unittest DISABLED_Remoting_Webapp_Js_Unittest
 #else
 #define MAYBE_Remoting_Webapp_Js_Unittest Remoting_Webapp_Js_Unittest
diff --git a/components/arc/common/notifications.mojom b/components/arc/common/notifications.mojom
index 6ee8f23..4d3eacfe 100644
--- a/components/arc/common/notifications.mojom
+++ b/components/arc/common/notifications.mojom
@@ -27,6 +27,11 @@
   MAX = PROGRESS
 };
 
+struct ArcNotificationButton {
+  // Title
+  string label;
+};
+
 struct ArcNotificationData {
   // Identifier of notification
   string key;
@@ -48,6 +53,8 @@
   int32 progress_current;
   // The maximum value of progress.
   int32 progress_max;
+  // Action buttons
+  array<ArcNotificationButton> buttons;
 };
 
 interface NotificationsHost {
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 65ed75ac..23e11ebc 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -20,8 +20,10 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/site_instance.h"
 #include "ipc/ipc_message_macros.h"
+#include "ui/gfx/geometry/size_f.h"
 
 namespace autofill {
 
@@ -147,6 +149,19 @@
     RendererShouldClearPreviewedForm();
 }
 
+gfx::RectF ContentAutofillDriver::TransformBoundingBoxToViewportCoordinates(
+    const gfx::RectF& bounding_box) {
+  gfx::Point orig_point(bounding_box.x(), bounding_box.y());
+  gfx::Point transformed_point;
+  transformed_point =
+      render_frame_host_->GetView()->TransformPointToRootCoordSpace(orig_point);
+
+  gfx::RectF new_box;
+  new_box.SetRect(transformed_point.x(), transformed_point.y(),
+                  bounding_box.width(), bounding_box.height());
+  return new_box;
+}
+
 bool ContentAutofillDriver::HandleMessage(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ContentAutofillDriver, message)
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index b9e9329..61bc690 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -61,6 +61,8 @@
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
   void PopupHidden() override;
+  gfx::RectF TransformBoundingBoxToViewportCoordinates(
+      const gfx::RectF& bounding_box) override;
 
   // Handles a message that came from the associated render frame.
   bool HandleMessage(const IPC::Message& message);
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index 82e0f12..cbead57 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -17,6 +17,10 @@
 class URLRequestContextGetter;
 }
 
+namespace gfx {
+class RectF;
+}
+
 namespace autofill {
 
 class FormStructure;
@@ -89,6 +93,12 @@
 
   // Informs the renderer that the popup has been hidden.
   virtual void PopupHidden() = 0;
+
+  // Transform bounding box coordinates to real viewport coordinates. In
+  // the case of a page spanning multiple renderer processes, subframe
+  // renderers cannot do this transformation themselves.
+  virtual gfx::RectF TransformBoundingBoxToViewportCoordinates(
+      const gfx::RectF& bounding_box) = 0;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_field.h b/components/autofill/core/browser/autofill_field.h
index 6c9d523..8295440e 100644
--- a/components/autofill/core/browser/autofill_field.h
+++ b/components/autofill/core/browser/autofill_field.h
@@ -53,8 +53,8 @@
   void set_previously_autofilled(bool previously_autofilled) {
     previously_autofilled_ = previously_autofilled;
   }
-  void set_parseable_name(base::string16 parseable_name) {
-    parseable_name_ = std::move(parseable_name);
+  void set_parseable_name(const base::string16& parseable_name) {
+    parseable_name_ = parseable_name;
   }
 
   // This function automatically chooses between server and heuristic autofill
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index fad9ab5..ff6f123 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -401,7 +401,9 @@
 
   std::vector<Suggestion> suggestions;
 
-  external_delegate_->OnQuery(query_id, form, field, bounding_box);
+  gfx::RectF transformed_box =
+      driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
+  external_delegate_->OnQuery(query_id, form, field, transformed_box);
 
   // Need to refresh models before using the form_event_loggers.
   bool is_autofill_possible = RefreshDataModels();
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index 38a83ed..906b55e 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/sequenced_worker_pool_owner.h"
 #include "base/threading/sequenced_worker_pool.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace autofill {
 
@@ -71,4 +72,9 @@
 void TestAutofillDriver::PopupHidden() {
 }
 
+gfx::RectF TestAutofillDriver::TransformBoundingBoxToViewportCoordinates(
+    const gfx::RectF& bounding_box) {
+  return bounding_box;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index 6282fa5..132196c 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -44,6 +44,8 @@
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
   void PopupHidden() override;
+  gfx::RectF TransformBoundingBoxToViewportCoordinates(
+      const gfx::RectF& bounding_box) override;
 
   // Methods that tests can use to specialize functionality.
 
diff --git a/components/autofill/core/common/password_form.cc b/components/autofill/core/common/password_form.cc
index 0ac9e33..5f8cbbf 100644
--- a/components/autofill/core/common/password_form.cc
+++ b/components/autofill/core/common/password_form.cc
@@ -83,13 +83,10 @@
       skip_zero_click(false),
       layout(Layout::LAYOUT_OTHER),
       was_parsed_using_autofill_predictions(false),
-      is_alive(true),
       is_public_suffix_match(false),
       is_affiliation_based_match(false) {}
 
 PasswordForm::~PasswordForm() {
-  CHECK(is_alive);
-  is_alive = false;
 }
 
 bool PasswordForm::IsPossibleChangePasswordForm() const {
diff --git a/components/autofill/core/common/password_form.h b/components/autofill/core/common/password_form.h
index a883bd9..66c58e4 100644
--- a/components/autofill/core/common/password_form.h
+++ b/components/autofill/core/common/password_form.h
@@ -259,9 +259,6 @@
   // If true, this form was parsed using Autofill predictions.
   bool was_parsed_using_autofill_predictions;
 
-  // TODO(vabr): Remove |is_alive| once http://crbug.com/486931 is fixed.
-  bool is_alive;  // Set on construction, reset on destruction.
-
   // If true, this match was found using public suffix matching.
   bool is_public_suffix_match;
 
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index 7c665f56..e89c9e6 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -58,6 +58,8 @@
   void RendererShouldPreviewFieldWithValue(
       const base::string16& value) override;
   void PopupHidden() override;
+  gfx::RectF TransformBoundingBoxToViewportCoordinates(
+      const gfx::RectF& bounding_box) override;
 
  private:
   AutofillDriverIOS(
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 1f32dddc..49a24f0f 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -8,6 +8,7 @@
 #include "ios/web/public/browser_state.h"
 #include "ios/web/public/web_state/web_state.h"
 #include "ios/web/public/web_thread.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 DEFINE_WEB_STATE_USER_DATA_KEY(autofill::AutofillDriverIOS);
 
@@ -98,4 +99,9 @@
 void AutofillDriverIOS::PopupHidden() {
 }
 
+gfx::RectF AutofillDriverIOS::TransformBoundingBoxToViewportCoordinates(
+    const gfx::RectF& bounding_box) {
+  return bounding_box;
+}
+
 }  // namespace autofill
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 95a8118b..ab7c93c 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -758,6 +758,7 @@
     ],
     'tracing_unittest_sources': [
       'tracing/graphics_memory_dump_provider_android_unittest.cc',
+      'tracing/process_metrics_memory_dump_provider_unittest.cc',
       'tracing/trace_config_file_unittest.cc',
     ],
     'translate_unittest_sources': [
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
index 0ede759..63365334 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
@@ -41,21 +41,6 @@
      * then {@link #build} is called to create the {@code CronetEngine}.
      */
     public static class Builder {
-        /**
-         * A class which provides a method for loading the cronet native library. Apps needing to
-         * implement custom library loading logic can inherit from this class and pass an instance
-         * to {@link CronetEngine.Builder#setLibraryLoader}. For example, this might be required
-         * to work around {@code UnsatisfiedLinkError}s caused by flaky installation on certain
-         * older devices.
-         */
-        public abstract static class LibraryLoader {
-            /**
-             * Loads the native library.
-             * @param libName name of the library to load
-             */
-            public abstract void loadLibrary(String libName);
-        }
-
         // A hint that a host supports QUIC.
         static class QuicHint {
             // The host.
@@ -101,7 +86,6 @@
         private String mUserAgent;
         private String mStoragePath;
         private boolean mLegacyModeEnabled;
-        private LibraryLoader mLibraryLoader;
         private String mLibraryName;
         private boolean mQuicEnabled;
         private boolean mHttp2Enabled;
@@ -203,22 +187,8 @@
             return this;
         }
 
-        /**
-         * Sets a {@link LibraryLoader} to be used to load the native library.
-         * If not set, the library will be loaded using {@link System#loadLibrary}.
-         * @return the builder to facilitate chaining.
-         */
-        public Builder setLibraryLoader(LibraryLoader loader) {
-            mLibraryLoader = loader;
-            return this;
-        }
-
-        void loadLibrary() {
-            if (mLibraryLoader == null) {
-                System.loadLibrary(mLibraryName);
-            } else {
-                mLibraryLoader.loadLibrary(mLibraryName);
-            }
+        String libraryName() {
+            return mLibraryName;
         }
 
         /**
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
index e5db0b42..e51334e 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
@@ -32,7 +32,7 @@
             if (sInitTaskPosted) {
                 return;
             }
-            builder.loadLibrary();
+            System.loadLibrary(builder.libraryName());
             if (!Version.CRONET_VERSION.equals(nativeGetCronetVersion())) {
                 throw new RuntimeException(String.format(
                       "Expected Cronet version number %s, "
@@ -74,7 +74,7 @@
         nativeCronetInitOnMainThread();
     }
 
-    // Native methods are implemented in cronet_library_loader.cc.
+    // Native methods are implemented in cronet_loader.cc.
     private static native void nativeCronetInitOnMainThread();
     private static native String nativeGetCronetVersion();
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 195eafcb..ce07d6b 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -960,35 +960,4 @@
     // Verifies that CronetEngine.Builder config from testCronetEngineBuilderConfig() is properly
     // translated to a native UrlRequestContextConfig.
     private static native void nativeVerifyUrlRequestContextConfig(long config, String storagePath);
-
-    private static class TestBadLibraryLoader extends CronetEngine.Builder.LibraryLoader {
-        private boolean mWasCalled = false;
-
-        public void loadLibrary(String libName) {
-            // Report that this method was called, but don't load the library
-            mWasCalled = true;
-        }
-
-        boolean wasCalled() {
-            return mWasCalled;
-        }
-    }
-
-    @SmallTest
-    @Feature({"Cronet"})
-    public void testSkipLibraryLoading() throws Exception {
-        CronetEngine.Builder builder = new CronetEngine.Builder(getContext());
-        TestBadLibraryLoader loader = new TestBadLibraryLoader();
-        builder.setLibraryLoader(loader).setLibraryName("cronet_tests");
-        try {
-            // ensureInitialized() calls native code to check the version right after library load
-            // and will error with the message below if library loading was skipped
-            CronetLibraryLoader.ensureInitialized(getContext(), builder);
-            fail("Native library should not be loaded");
-        } catch (UnsatisfiedLinkError e) {
-            assertTrue(e.getMessage().contains(
-                    "No implementation found for java.lang.String org.chromium.net"));
-            assertTrue(loader.wasCalled());
-        }
-    }
 }
diff --git a/components/html_viewer/html_frame.cc b/components/html_viewer/html_frame.cc
index 4ee31a77..9cd0255 100644
--- a/components/html_viewer/html_frame.cc
+++ b/components/html_viewer/html_frame.cc
@@ -316,7 +316,8 @@
     blink::WebMediaPlayerClient* client,
     blink::WebMediaPlayerEncryptedMediaClient* encrypted_client,
     blink::WebContentDecryptionModule* initial_cdm,
-    const blink::WebString& sink_id) {
+    const blink::WebString& sink_id,
+    blink::WebMediaSession* media_session) {
   return global_state()->media_factory()->CreateMediaPlayer(
       frame, url, client, encrypted_client, initial_cdm, GetApp()->shell());
 }
diff --git a/components/html_viewer/html_frame.h b/components/html_viewer/html_frame.h
index b9c0137e..1d7e7c3 100644
--- a/components/html_viewer/html_frame.h
+++ b/components/html_viewer/html_frame.h
@@ -167,7 +167,8 @@
       blink::WebMediaPlayerClient* client,
       blink::WebMediaPlayerEncryptedMediaClient* encrypted_client,
       blink::WebContentDecryptionModule* initial_cdm,
-      const blink::WebString& sink_id) override;
+      const blink::WebString& sink_id,
+      blink::WebMediaSession* media_session) override;
   blink::WebFrame* createChildFrame(
       blink::WebLocalFrame* parent,
       blink::WebTreeScopeType scope,
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index e1fee32d..846cca42 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -418,108 +418,50 @@
     }
   }
   logins_result.erase(begin_blacklisted, logins_result.end());
+  if (logins_result.empty())
+    return;
 
   // Now compute scores for the remaining credentials in |login_result|.
   std::vector<uint32_t> credential_scores;
   credential_scores.reserve(logins_result.size());
   uint32_t best_score = 0;
+  std::map<base::string16, uint32_t> best_scores;
   for (const PasswordForm* login : logins_result) {
     uint32_t current_score = ScoreResult(*login);
-    if (current_score > best_score)
-      best_score = current_score;
+    best_score = std::max(best_score, current_score);
+    best_scores[login->username_value] =
+        std::max(best_scores[login->username_value], current_score);
     credential_scores.push_back(current_score);
   }
 
-  if (best_score == 0) {
-    if (logger) {
-      logger->LogNumber(Logger::STRING_BEST_SCORE,
-                        static_cast<size_t>(best_score));
-    }
-    return;
-  }
-
-  // Start the |best_matches_| with the best-scoring normal credentials and save
-  // the worse-scoring "protected" ones for later.
-  ScopedVector<PasswordForm> protected_credentials;
+  // Fill |best_matches_| with the best-scoring credentials for each username.
   for (size_t i = 0; i < logins_result.size(); ++i) {
     // Take ownership of the PasswordForm from the ScopedVector.
     scoped_ptr<PasswordForm> login(logins_result[i]);
     logins_result[i] = nullptr;
     DCHECK(!login->blacklisted_by_user);
+    const base::string16& username = login->username_value;
 
-    if (credential_scores[i] < best_score) {
-      // Empty path matches are most commonly imports from Firefox, and
-      // generally useful to autofill. Blacklisted entries are only meaningful
-      // in the absence of non-blacklisted entries, in which case they need no
-      // protection to become |best_matches_|. TODO(timsteele): Bug 1269400. We
-      // probably should do something more elegant for any shorter-path match
-      // instead of explicitly handling empty path matches.
-      bool is_credential_protected =
-          observed_form_.scheme == PasswordForm::SCHEME_HTML &&
-          base::StartsWith("/", login->origin.path(),
-                           base::CompareCase::SENSITIVE) &&
-          credential_scores[i] > 0;
-      // Passwords generated on a signup form must show on a login form even if
-      // there are better-matching saved credentials. TODO(gcasto): We don't
-      // want to cut credentials that were saved on signup forms even if they
-      // weren't generated, but currently it's hard to distinguish between those
-      // forms and two different login forms on the same domain. Filed
-      // http://crbug.com/294468 to look into this.
-      is_credential_protected |= login->type == PasswordForm::TYPE_GENERATED;
-
-      // Websites that participate in affiliation-based matching will normally
-      // have a single authentication system per domain, therefore affiliation
-      // based matches are desired to be offered on any login form on the site.
-      // However, for Android credentials, most meta-data attributes are empty,
-      // so they will have a very low score, hence need to be protected against
-      // the high-scoring logins saved from the website.
-      is_credential_protected |= IsValidAndroidFacetURI(login->signon_realm);
-
-      if (is_credential_protected)
-        protected_credentials.push_back(std::move(login));
-      else
-        not_best_matches_.push_back(std::move(login));
+    if (credential_scores[i] < best_scores[username]) {
+      not_best_matches_.push_back(std::move(login));
       continue;
     }
 
+    if (!preferred_match_ && credential_scores[i] == best_score)
+      preferred_match_ = login.get();
+
     // If there is another best-score match for the same username then leave it
     // and add the current form to |not_best_matches_|.
-    const base::string16& username = login->username_value;
     auto best_match_username = best_matches_.find(username);
     if (best_match_username == best_matches_.end()) {
-      if (login->preferred)
-        preferred_match_ = login.get();
       best_matches_.insert(std::make_pair(username, std::move(login)));
     } else {
       not_best_matches_.push_back(std::move(login));
     }
   }
 
-  // Add the protected results if we don't already have a result with the same
-  // username.
-  for (ScopedVector<PasswordForm>::iterator it = protected_credentials.begin();
-       it != protected_credentials.end(); ++it) {
-    // Take ownership of the PasswordForm from the ScopedVector.
-    scoped_ptr<PasswordForm> protege(*it);
-    *it = nullptr;
-    const base::string16& username = protege->username_value;
-    if (best_matches_.find(username) == best_matches_.end())
-      best_matches_.insert(std::make_pair(username, std::move(protege)));
-    else
-      not_best_matches_.push_back(std::move(protege));
-  }
-
   UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown",
                        logins_result_size - best_matches_.size());
-
-  if (!best_matches_.empty()) {
-    // It is possible we have at least one match but have no preferred_match_,
-    // because a user may have chosen to 'Forget' the preferred match. So we
-    // just pick the first one and whichever the user selects for submit will
-    // be saved as preferred.
-    if (!preferred_match_)
-      preferred_match_ = best_matches_.begin()->second.get();
-  }
 }
 
 void PasswordFormManager::ProcessFrame(
@@ -686,7 +628,7 @@
   PasswordFormMap::const_iterator iter;
   for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) {
     if (iter->second->username_value != pending_credentials_.username_value &&
-        iter->second->preferred) {
+        iter->second->preferred && !iter->second->is_public_suffix_match) {
       // This wasn't the selected login but it used to be preferred.
       iter->second->preferred = false;
       if (user_action_ == kUserActionNone)
@@ -1146,8 +1088,12 @@
 
   uint32_t score = 0u;
   if (!candidate.is_public_suffix_match) {
-    score += 1u << 7;
+    score += 1u << 8;
   }
+
+  if (candidate.preferred)
+    score += 1u << 7;
+
   if (candidate.origin == observed_form_.origin) {
     // This check is here for the most common case which
     // is we have a single match in the db for the given host,
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 56af53e..1d94403 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -1116,18 +1116,32 @@
   form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
 }
 
-TEST_F(PasswordFormManagerTest, TestForceInclusionOfGeneratedPasswords_Match) {
-  // Simulate having two matches for this origin, one of which was from a form
-  // with different HTML tags for elements. Because of scoring differences,
-  // only the first form will be sent to Autofill().
-  EXPECT_CALL(*client()->mock_driver(), AllowPasswordGenerationForForm(_));
-
+TEST_F(PasswordFormManagerTest, TestBestCredentialsByEachUsernameAreIncluded) {
+  // Simulate having several matches, with 3 different usernames. Some of the
+  // matches are PSL matches. One match for each username should be chosen.
   ScopedVector<PasswordForm> simulated_results;
-  simulated_results.push_back(CreateSavedMatch(false));
-  simulated_results.push_back(CreateSavedMatch(false));
-  simulated_results[1]->username_value = ASCIIToUTF16("other@gmail.com");
+  // Add a best scoring match. It should be in |best_matches| and chosen as a
+  // prefferred match.
+  simulated_results.push_back(new PasswordForm(*saved_match()));
+  // Add a match saved on another form, it has lower score. It should not be in
+  // |best_matches|.
+  simulated_results.push_back(new PasswordForm(*saved_match()));
   simulated_results[1]->password_element = ASCIIToUTF16("signup_password");
   simulated_results[1]->username_element = ASCIIToUTF16("signup_username");
+  // Add a match saved on another form with a different username. It should be
+  // in |best_matches|.
+  simulated_results.push_back(new PasswordForm(*saved_match()));
+  auto username1 = simulated_results[0]->username_value + ASCIIToUTF16("1");
+  simulated_results[2]->username_value = username1;
+  simulated_results[2]->password_element = ASCIIToUTF16("signup_password");
+  simulated_results[2]->username_element = ASCIIToUTF16("signup_username");
+  // Add a PSL match, it should not be in |best_matches|.
+  simulated_results.push_back(new PasswordForm(*psl_saved_match()));
+  // Add a PSL match with a different username. It should be in |best_matches|.
+  simulated_results.push_back(new PasswordForm(*psl_saved_match()));
+  auto username2 = simulated_results[0]->username_value + ASCIIToUTF16("2");
+  simulated_results[4]->username_value = username2;
+
   form_manager()->SimulateFetchMatchingLoginsFromPasswordStore();
 
   autofill::PasswordFormFillData fill_data;
@@ -1135,8 +1149,18 @@
       .WillOnce(SaveArg<0>(&fill_data));
 
   form_manager()->OnGetPasswordStoreResults(std::move(simulated_results));
-  EXPECT_EQ(1u, form_manager()->best_matches().size());
-  EXPECT_TRUE(fill_data.additional_logins.empty());
+  const autofill::PasswordFormMap& best_matches =
+      form_manager()->best_matches();
+  EXPECT_EQ(3u, best_matches.size());
+  EXPECT_NE(best_matches.end(),
+            best_matches.find(saved_match()->username_value));
+  EXPECT_EQ(*saved_match(),
+            *best_matches.find(saved_match()->username_value)->second);
+  EXPECT_NE(best_matches.end(), best_matches.find(username1));
+  EXPECT_NE(best_matches.end(), best_matches.find(username2));
+
+  EXPECT_EQ(*saved_match(), *form_manager()->preferred_match());
+  EXPECT_EQ(2u, fill_data.additional_logins.size());
 }
 
 TEST_F(PasswordFormManagerTest,
diff --git a/components/policy/core/common/cloud/enterprise_metrics.cc b/components/policy/core/common/cloud/enterprise_metrics.cc
index e0cedf2c..a1b0f3f 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.cc
+++ b/components/policy/core/common/cloud/enterprise_metrics.cc
@@ -13,6 +13,5 @@
 const char kMetricDevicePolicyRefresh[] = "Enterprise.DevicePolicyRefresh";
 const char kMetricDevicePolicyInvalidations[] =
     "Enterprise.DevicePolicyInvalidations";
-const char kMetricSystemLogPII[] = "Enterprise.SystemLogPIILeak";
 
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h
index ae51e2b7..179feaf 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.h
+++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -265,24 +265,6 @@
   POLICY_INVALIDATION_TYPE_SIZE  // Must be the last.
 };
 
-// Types of sensitive data that is removed from system logs to upload.
-// This enum is used to define the buckets for an enumerated UMA histogram.
-// Hence,
-// (a) existing enumerated constants should never be deleted or reordered, and
-// (b) new constants should only be appended at the end of the enumeration.
-enum SystemLogPIIType {
-  // Found email address in system logs to  upload.
-  SYSTEM_LOG_PII_TYPE_EMAIL_ADDRESS = 0,
-  // Found IP address in system logs to upload.
-  SYSTEM_LOG_PII_TYPE_IP_ADDRESS = 1,
-  // Found Web URL in system logs to upload.
-  SYSTEM_LOG_PII_TYPE_WEB_URL = 2,
-  // Found SSID in system logs to upload.
-  SYSTEM_LOG_PII_TYPE_SSID = 3,
-
-  SYSTEM_LOG_PII_TYPE_SIZE  // Must be the last.
-};
-
 // Names for the UMA counters. They are shared from here since the events
 // from the same enum above can be triggered in different files, and must use
 // the same UMA histogram name.
@@ -292,7 +274,6 @@
 POLICY_EXPORT extern const char kMetricUserPolicyInvalidations[];
 POLICY_EXPORT extern const char kMetricDevicePolicyRefresh[];
 POLICY_EXPORT extern const char kMetricDevicePolicyInvalidations[];
-POLICY_EXPORT extern const char kMetricSystemLogPII[];
 
 }  // namespace policy
 
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc
index 7334f74..2ea3db6a 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
@@ -632,16 +632,12 @@
 
   bool loading_tasks_seem_expensive = false;
   bool timer_tasks_seem_expensive = false;
-  // Only deem tasks to be exensive (which may cause them to be preemptively
-  // blocked) if we are expecting frames.
-  if (!MainThreadOnly().begin_frame_not_expected_soon) {
-    loading_tasks_seem_expensive =
-        MainThreadOnly().loading_task_cost_estimator.expected_task_duration() >
-        longest_jank_free_task_duration;
-    timer_tasks_seem_expensive =
-        MainThreadOnly().timer_task_cost_estimator.expected_task_duration() >
-        longest_jank_free_task_duration;
-  }
+  loading_tasks_seem_expensive =
+      MainThreadOnly().loading_task_cost_estimator.expected_task_duration() >
+      longest_jank_free_task_duration;
+  timer_tasks_seem_expensive =
+      MainThreadOnly().timer_task_cost_estimator.expected_task_duration() >
+      longest_jank_free_task_duration;
   MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
   MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive;
 
diff --git a/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc
index cc6616b..0c73012 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc
+++ b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -2282,7 +2282,7 @@
 }
 
 TEST_F(RendererSchedulerImplTest,
-       ExpensiveTimerTaskNotBlocked_IfBeginMainFrameNotExpectedSoon) {
+       ExpensiveTimerTaskBlocked_EvenIfBeginMainFrameNotExpectedSoon) {
   std::vector<std::string> run_order;
 
   scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
@@ -2297,10 +2297,9 @@
   EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_TRUE(HaveSeenABeginMainframe());
   EXPECT_FALSE(LoadingTasksSeemExpensive());
-  EXPECT_FALSE(TimerTasksSeemExpensive());
+  EXPECT_TRUE(TimerTasksSeemExpensive());
   EXPECT_TRUE(TouchStartExpectedSoon());
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("D1")));
+  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
 }
 
 TEST_F(RendererSchedulerImplTest,
diff --git a/components/tracing.gyp b/components/tracing.gyp
index d0d7677..9466a1c8 100644
--- a/components/tracing.gyp
+++ b/components/tracing.gyp
@@ -32,6 +32,8 @@
         'tracing/child_trace_message_filter.h',
         'tracing/graphics_memory_dump_provider_android.cc',
         'tracing/graphics_memory_dump_provider_android.h',
+        'tracing/process_metrics_memory_dump_provider.cc',
+        'tracing/process_metrics_memory_dump_provider.h',
         'tracing/trace_config_file.cc',
         'tracing/trace_config_file.h',
         'tracing/trace_to_console.cc',
@@ -42,6 +44,13 @@
         'tracing/tracing_switches.cc',
         'tracing/tracing_switches.h',
       ],
+      'target_conditions': [
+        ['>(nacl_untrusted_build)==1', {
+          'sources!': [
+            'tracing/process_metrics_memory_dump_provider.cc',
+          ],
+        }],
+      ]
     },
   ],
 }
diff --git a/components/tracing/BUILD.gn b/components/tracing/BUILD.gn
index 0111de9..927aeb67 100644
--- a/components/tracing/BUILD.gn
+++ b/components/tracing/BUILD.gn
@@ -10,6 +10,8 @@
     "child_trace_message_filter.h",
     "graphics_memory_dump_provider_android.cc",
     "graphics_memory_dump_provider_android.h",
+    "process_metrics_memory_dump_provider.cc",
+    "process_metrics_memory_dump_provider.h",
     "tracing_export.h",
     "tracing_messages.cc",
     "tracing_messages.h",
@@ -21,6 +23,10 @@
     "//base",
     "//ipc",
   ]
+
+  if (is_nacl) {
+    sources -= [ "process_metrics_memory_dump_provider.cc" ]
+  }
 }
 
 component("startup_tracing") {
@@ -46,6 +52,7 @@
 
   sources = [
     "graphics_memory_dump_provider_android_unittest.cc",
+    "process_metrics_memory_dump_provider_unittest.cc",
   ]
 
   deps = [
diff --git a/components/tracing/child_memory_dump_manager_delegate_impl.cc b/components/tracing/child_memory_dump_manager_delegate_impl.cc
index 05c26627..ced940b 100644
--- a/components/tracing/child_memory_dump_manager_delegate_impl.cc
+++ b/components/tracing/child_memory_dump_manager_delegate_impl.cc
@@ -5,7 +5,9 @@
 #include "components/tracing/child_memory_dump_manager_delegate_impl.h"
 
 #include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
 #include "components/tracing/child_trace_message_filter.h"
+#include "components/tracing/process_metrics_memory_dump_provider.h"
 
 namespace tracing {
 
@@ -49,6 +51,13 @@
   if (ctmf) {
     base::trace_event::MemoryDumpManager::GetInstance()->Initialize(
       this /* delegate */, false /* is_coordinator */);
+
+#if !defined(OS_LINUX) && !defined(OS_NACL)
+    // On linux the browser process takes care of dumping process metrics.
+    // The child process is not allowed to do so due to BPF sandbox.
+    tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+        base::kNullProcessId);
+#endif
   }
 }
 
diff --git a/components/tracing/process_metrics_memory_dump_provider.cc b/components/tracing/process_metrics_memory_dump_provider.cc
new file mode 100644
index 0000000..e73ea78
--- /dev/null
+++ b/components/tracing/process_metrics_memory_dump_provider.cc
@@ -0,0 +1,305 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/tracing/process_metrics_memory_dump_provider.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_maps.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "build/build_config.h"
+
+namespace tracing {
+
+namespace {
+
+base::LazyInstance<
+    std::map<base::ProcessId, scoped_ptr<ProcessMetricsMemoryDumpProvider>>>::
+    Leaky g_dump_providers_map = LAZY_INSTANCE_INITIALIZER;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+const char kClearPeakRssCommand[] = "5";
+
+const uint32_t kMaxLineSize = 4096;
+
+bool ParseSmapsHeader(const char* header_line,
+                      base::trace_event::ProcessMemoryMaps::VMRegion* region) {
+  // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234  /foo.so\n"
+  bool res = true;  // Whether this region should be appended or skipped.
+  uint64_t end_addr = 0;
+  char protection_flags[5] = {0};
+  char mapped_file[kMaxLineSize];
+
+  if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
+             &region->start_address, &end_addr, protection_flags,
+             mapped_file) != 4)
+    return false;
+
+  if (end_addr > region->start_address) {
+    region->size_in_bytes = end_addr - region->start_address;
+  } else {
+    // This is not just paranoia, it can actually happen (See crbug.com/461237).
+    region->size_in_bytes = 0;
+    res = false;
+  }
+
+  region->protection_flags = 0;
+  if (protection_flags[0] == 'r') {
+    region->protection_flags |=
+        base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+  }
+  if (protection_flags[1] == 'w') {
+    region->protection_flags |=
+        base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+  }
+  if (protection_flags[2] == 'x') {
+    region->protection_flags |=
+        base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+  }
+
+  region->mapped_file = mapped_file;
+  base::TrimWhitespaceASCII(region->mapped_file, base::TRIM_ALL,
+                            &region->mapped_file);
+
+  return res;
+}
+
+uint64_t ReadCounterBytes(char* counter_line) {
+  uint64_t counter_value = 0;
+  int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
+  return res == 1 ? counter_value * 1024 : 0;
+}
+
+uint32_t ParseSmapsCounter(
+    char* counter_line,
+    base::trace_event::ProcessMemoryMaps::VMRegion* region) {
+  // A smaps counter lines looks as follows: "RSS:  0 Kb\n"
+  uint32_t res = 1;
+  char counter_name[20];
+  int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
+  if (did_read != 1)
+    return 0;
+
+  if (strcmp(counter_name, "Pss:") == 0) {
+    region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
+  } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
+    region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
+  } else if (strcmp(counter_name, "Private_Clean:") == 0) {
+    region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
+  } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
+    region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
+  } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
+    region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
+  } else if (strcmp(counter_name, "Swap:") == 0) {
+    region->byte_stats_swapped = ReadCounterBytes(counter_line);
+  } else {
+    res = 0;
+  }
+
+  return res;
+}
+
+uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file,
+                                base::trace_event::ProcessMemoryMaps* pmm) {
+  if (!smaps_file)
+    return 0;
+
+  fseek(smaps_file, 0, SEEK_SET);
+
+  char line[kMaxLineSize];
+  const uint32_t kNumExpectedCountersPerRegion = 6;
+  uint32_t counters_parsed_for_current_region = 0;
+  uint32_t num_valid_regions = 0;
+  base::trace_event::ProcessMemoryMaps::VMRegion region;
+  bool should_add_current_region = false;
+  for (;;) {
+    line[0] = '\0';
+    if (fgets(line, kMaxLineSize, smaps_file) == nullptr || !strlen(line))
+      break;
+    if (isxdigit(line[0]) && !isupper(line[0])) {
+      region = base::trace_event::ProcessMemoryMaps::VMRegion();
+      counters_parsed_for_current_region = 0;
+      should_add_current_region = ParseSmapsHeader(line, &region);
+    } else {
+      counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
+      DCHECK_LE(counters_parsed_for_current_region,
+                kNumExpectedCountersPerRegion);
+      if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
+        if (should_add_current_region) {
+          pmm->AddVMRegion(region);
+          ++num_valid_regions;
+          should_add_current_region = false;
+        }
+      }
+    }
+  }
+  return num_valid_regions;
+}
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
+
+scoped_ptr<base::ProcessMetrics> CreateProcessMetrics(base::ProcessId process) {
+  if (process == base::kNullProcessId)
+    return make_scoped_ptr(base::ProcessMetrics::CreateCurrentProcessMetrics());
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  // Just pass ProcessId instead of handle since they are the same in linux and
+  // android.
+  return make_scoped_ptr(base::ProcessMetrics::CreateProcessMetrics(process));
+#else
+  // Creating process metrics for child processes in mac or windows requires
+  // additional information like ProcessHandle or port provider. This is a non
+  // needed use case.
+  NOTREACHED();
+  return scoped_ptr<base::ProcessMetrics>();
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
+}
+
+}  // namespace
+
+// static
+uint64_t ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+
+// static
+FILE* ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = nullptr;
+
+bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  uint32_t res = 0;
+  if (proc_smaps_for_testing) {
+    res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
+  } else {
+    std::string file_name = "/proc/" + (process_ == base::kNullProcessId
+                                            ? "self"
+                                            : base::IntToString(process_)) +
+                            "/smaps";
+    base::ScopedFILE smaps_file(fopen(file_name.c_str(), "r"));
+    res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
+  }
+
+  if (res)
+    pmd->set_has_process_mmaps();
+  return res;
+}
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// static
+void ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+    base::ProcessId process) {
+  scoped_ptr<ProcessMetricsMemoryDumpProvider> metrics_provider(
+      new ProcessMetricsMemoryDumpProvider(process));
+  base::trace_event::MemoryDumpProvider::Options options(process);
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      metrics_provider.get(), "ProcessMemoryMetrics", nullptr, options);
+  bool did_insert =
+      g_dump_providers_map.Get()
+          .insert(std::make_pair(process, std::move(metrics_provider)))
+          .second;
+  if (!did_insert) {
+    DLOG(ERROR) << "ProcessMetricsMemoryDumpProvider already registered for "
+                << (process == base::kNullProcessId
+                        ? "current process"
+                        : "process id " + base::IntToString(process));
+  }
+}
+
+// static
+void ProcessMetricsMemoryDumpProvider::UnregisterForProcess(
+    base::ProcessId process) {
+  auto iter = g_dump_providers_map.Get().find(process);
+  if (iter == g_dump_providers_map.Get().end()) {
+    return;
+  }
+  base::trace_event::MemoryDumpManager::GetInstance()
+      ->UnregisterAndDeleteDumpProviderSoon(std::move(iter->second));
+  g_dump_providers_map.Get().erase(iter);
+}
+
+ProcessMetricsMemoryDumpProvider::ProcessMetricsMemoryDumpProvider(
+    base::ProcessId process)
+    : process_(process),
+      process_metrics_(CreateProcessMetrics(process)),
+      is_rss_peak_resettable_(true) {}
+
+ProcessMetricsMemoryDumpProvider::~ProcessMetricsMemoryDumpProvider() {}
+
+// Called at trace dump point time. Creates a snapshot of the memory maps for
+// the current process.
+bool ProcessMetricsMemoryDumpProvider::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  bool res = DumpProcessTotals(args, pmd);
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  if (args.level_of_detail ==
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED)
+    res &= DumpProcessMemoryMaps(args, pmd);
+#endif
+  return res;
+}
+
+bool ProcessMetricsMemoryDumpProvider::DumpProcessTotals(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  const uint64_t rss_bytes = rss_bytes_for_testing
+                                 ? rss_bytes_for_testing
+                                 : process_metrics_->GetWorkingSetSize();
+
+  // rss_bytes will be 0 if the process ended while dumping.
+  if (!rss_bytes)
+    return false;
+
+  uint64_t peak_rss_bytes = 0;
+
+#if !defined(OS_IOS)
+  peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  if (is_rss_peak_resettable_) {
+    std::string clear_refs_file =
+        "/proc/" +
+        (process_ == base::kNullProcessId ? "self"
+                                          : base::IntToString(process_)) +
+        "/clear_refs";
+    int clear_refs_fd = open(clear_refs_file.c_str(), O_WRONLY);
+    if (clear_refs_fd > 0 &&
+        base::WriteFileDescriptor(clear_refs_fd, kClearPeakRssCommand,
+                                  sizeof(kClearPeakRssCommand))) {
+      pmd->process_totals()->set_is_peak_rss_resetable(true);
+    } else {
+      is_rss_peak_resettable_ = false;
+    }
+    close(clear_refs_fd);
+  }
+#elif defined(MACOSX)
+  size_t private_bytes;
+  bool res = process_metrics_->GetMemoryBytes(&private_bytes,
+                                              nullptr /* shared_bytes */);
+  if (res)
+    pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
+#endif  // !defined(OS_IOS)
+
+  pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+  pmd->set_has_process_totals();
+  pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
+
+  // Returns true even if other metrics failed, since rss is reported.
+  return true;
+}
+
+}  // namespace tracing
diff --git a/components/tracing/process_metrics_memory_dump_provider.h b/components/tracing/process_metrics_memory_dump_provider.h
new file mode 100644
index 0000000..b002d10
--- /dev/null
+++ b/components/tracing/process_metrics_memory_dump_provider.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TRACING_PROCESS_MEMORY_METRICS_DUMP_PROVIDER_H_
+#define COMPONENTS_TRACING_PROCESS_MEMORY_METRICS_DUMP_PROVIDER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "build/build_config.h"
+#include "components/tracing/tracing_export.h"
+
+namespace base {
+class ProcessMetrics;
+}
+
+namespace tracing {
+
+// Dump provider which collects process-wide memory stats.
+class TRACING_EXPORT ProcessMetricsMemoryDumpProvider
+    : public base::trace_event::MemoryDumpProvider {
+ public:
+  // Pass base::kNullProcessId to register for current process.
+  static void RegisterForProcess(base::ProcessId process);
+  static void UnregisterForProcess(base::ProcessId process);
+
+  ~ProcessMetricsMemoryDumpProvider() override;
+
+  // MemoryDumpProvider implementation.
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest,
+                           ParseProcSmaps);
+  FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, DumpRSS);
+
+  ProcessMetricsMemoryDumpProvider(base::ProcessId process);
+
+  bool DumpProcessTotals(const base::trace_event::MemoryDumpArgs& args,
+                         base::trace_event::ProcessMemoryDump* pmd);
+  bool DumpProcessMemoryMaps(const base::trace_event::MemoryDumpArgs& args,
+                             base::trace_event::ProcessMemoryDump* pmd);
+
+  static uint64_t rss_bytes_for_testing;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  static FILE* proc_smaps_for_testing;
+#endif
+
+  base::ProcessId process_;
+  scoped_ptr<base::ProcessMetrics> process_metrics_;
+
+  // The peak may not be resettable on all the processes if the linux kernel is
+  // older than http://bit.ly/reset_rss or only on child processes if yama LSM
+  // sandbox is enabled.
+  bool is_rss_peak_resettable_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMetricsMemoryDumpProvider);
+};
+
+}  // namespace tracing
+
+#endif  // COMPONENTS_TRACING_PROCESS_MEMORY_METRICS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/components/tracing/process_metrics_memory_dump_provider_unittest.cc
similarity index 68%
rename from base/trace_event/process_memory_maps_dump_provider_unittest.cc
rename to components/tracing/process_metrics_memory_dump_provider_unittest.cc
index 624f96f..62e8fd6 100644
--- a/base/trace_event/process_memory_maps_dump_provider_unittest.cc
+++ b/components/tracing/process_metrics_memory_dump_provider_unittest.cc
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/trace_event/process_memory_maps_dump_provider.h"
+#include "components/tracing/process_metrics_memory_dump_provider.h"
 
 #include <stdint.h>
 
 #include "base/files/file_util.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/process_memory_maps.h"
+#include "base/trace_event/process_memory_totals.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace base {
-namespace trace_event {
+namespace tracing {
 
+#if defined(OS_LINUX) || defined(OS_ANDROID)
 namespace {
 const char kTestSmaps1[] =
     "00400000-004be000 r-xp 00000000 fc:01 1234              /file/1\n"
@@ -105,8 +106,8 @@
     "VmFlags: rd wr mr mw me ac sd\n";
 
 void CreateAndSetSmapsFileForTesting(const char* smaps_string,
-                                     ScopedFILE& file) {
-  FilePath temp_path;
+                                     base::ScopedFILE& file) {
+  base::FilePath temp_path;
   FILE* temp_file = CreateAndOpenTemporaryFile(&temp_path);
   file.reset(temp_file);
   ASSERT_TRUE(temp_file);
@@ -116,28 +117,69 @@
 }
 
 }  // namespace
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
-TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) {
-  const uint32_t kProtR = ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
-  const uint32_t kProtW = ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
-  const uint32_t kProtX = ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
-  const MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+TEST(ProcessMetricsMemoryDumpProviderTest, DumpRSS) {
+  const base::trace_event::MemoryDumpArgs high_detail_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  scoped_ptr<ProcessMetricsMemoryDumpProvider> pmtdp(
+      new ProcessMetricsMemoryDumpProvider(base::kNullProcessId));
+  scoped_ptr<base::trace_event::ProcessMemoryDump> pmd_before(
+      new base::trace_event::ProcessMemoryDump(nullptr));
+  scoped_ptr<base::trace_event::ProcessMemoryDump> pmd_after(
+      new base::trace_event::ProcessMemoryDump(nullptr));
 
-  auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance();
+  ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 1024;
+  pmtdp->OnMemoryDump(high_detail_args, pmd_before.get());
+
+  // Pretend that the RSS of the process increased of +1M.
+  const size_t kAllocSize = 1048576;
+  ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing += kAllocSize;
+
+  pmtdp->OnMemoryDump(high_detail_args, pmd_after.get());
+
+  ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0;
+
+  ASSERT_TRUE(pmd_before->has_process_totals());
+  ASSERT_TRUE(pmd_after->has_process_totals());
+
+  const uint64_t rss_before =
+      pmd_before->process_totals()->resident_set_bytes();
+  const uint64_t rss_after = pmd_after->process_totals()->resident_set_bytes();
+
+  EXPECT_NE(0U, rss_before);
+  EXPECT_NE(0U, rss_after);
+
+  EXPECT_EQ(rss_after - rss_before, kAllocSize);
+}
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) {
+  const uint32_t kProtR =
+      base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+  const uint32_t kProtW =
+      base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+  const uint32_t kProtX =
+      base::trace_event::ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+  const base::trace_event::MemoryDumpArgs dump_args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+
+  scoped_ptr<ProcessMetricsMemoryDumpProvider> pmmdp(
+      new ProcessMetricsMemoryDumpProvider(base::kNullProcessId));
 
   // Emulate an empty /proc/self/smaps.
-  ProcessMemoryDump pmd_invalid(nullptr /* session_state */);
-  ScopedFILE empty_file(OpenFile(FilePath("/dev/null"), "r"));
+  base::trace_event::ProcessMemoryDump pmd_invalid(nullptr /* session_state */);
+  base::ScopedFILE empty_file(OpenFile(base::FilePath("/dev/null"), "r"));
   ASSERT_TRUE(empty_file.get());
-  ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = empty_file.get();
+  ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = empty_file.get();
   pmmdp->OnMemoryDump(dump_args, &pmd_invalid);
   ASSERT_FALSE(pmd_invalid.has_process_mmaps());
 
   // Parse the 1st smaps file.
-  ProcessMemoryDump pmd_1(nullptr /* session_state */);
-  ScopedFILE temp_file1;
+  base::trace_event::ProcessMemoryDump pmd_1(nullptr /* session_state */);
+  base::ScopedFILE temp_file1;
   CreateAndSetSmapsFileForTesting(kTestSmaps1, temp_file1);
-  ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = temp_file1.get();
+  ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = temp_file1.get();
   pmmdp->OnMemoryDump(dump_args, &pmd_1);
   ASSERT_TRUE(pmd_1.has_process_mmaps());
   const auto& regions_1 = pmd_1.process_mmaps()->vm_regions();
@@ -166,10 +208,10 @@
   EXPECT_EQ(0 * 1024UL, regions_1[1].byte_stats_swapped);
 
   // Parse the 2nd smaps file.
-  ProcessMemoryDump pmd_2(nullptr /* session_state */);
-  ScopedFILE temp_file2;
+  base::trace_event::ProcessMemoryDump pmd_2(nullptr /* session_state */);
+  base::ScopedFILE temp_file2;
   CreateAndSetSmapsFileForTesting(kTestSmaps2, temp_file2);
-  ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = temp_file2.get();
+  ProcessMetricsMemoryDumpProvider::proc_smaps_for_testing = temp_file2.get();
   pmmdp->OnMemoryDump(dump_args, &pmd_2);
   ASSERT_TRUE(pmd_2.has_process_mmaps());
   const auto& regions_2 = pmd_2.process_mmaps()->vm_regions();
@@ -185,6 +227,6 @@
   EXPECT_EQ(4 * 1024UL, regions_2[0].byte_stats_private_dirty_resident);
   EXPECT_EQ(0 * 1024UL, regions_2[0].byte_stats_swapped);
 }
+#endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
-}  // namespace trace_event
-}  // namespace base
+}  // namespace tracing
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index e815e85..141eab8 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -32,6 +32,7 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "components/tracing/process_metrics_memory_dump_provider.h"
 #include "components/tracing/trace_config_file.h"
 #include "components/tracing/trace_to_console.h"
 #include "components/tracing/tracing_switches.h"
@@ -696,6 +697,8 @@
 
   // Enable memory-infra dump providers.
   InitSkiaEventTracer();
+  tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+      base::kNullProcessId);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       HostSharedBitmapManager::current(), "HostSharedBitmapManager", nullptr);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 1d8b23c0..fef7ff7 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -172,15 +172,15 @@
     root_view->UpdateCursor(cursor);
 }
 
-void CrossProcessFrameConnector::TransformPointToRootCoordSpace(
+gfx::Point CrossProcessFrameConnector::TransformPointToRootCoordSpace(
     const gfx::Point& point,
-    cc::SurfaceId surface_id,
-    gfx::Point* transformed_point) {
+    cc::SurfaceId surface_id) {
+  gfx::Point transformed_point = point;
   RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView();
-  *transformed_point = point;
   if (root_view)
     root_view->TransformPointToLocalCoordSpace(point, surface_id,
-                                               transformed_point);
+                                               &transformed_point);
+  return transformed_point;
 }
 
 bool CrossProcessFrameConnector::HasFocus() {
diff --git a/content/browser/frame_host/cross_process_frame_connector.h b/content/browser/frame_host/cross_process_frame_connector.h
index 6f31ec60..6ad56a21 100644
--- a/content/browser/frame_host/cross_process_frame_connector.h
+++ b/content/browser/frame_host/cross_process_frame_connector.h
@@ -101,9 +101,8 @@
   float device_scale_factor() const { return device_scale_factor_; }
   void GetScreenInfo(blink::WebScreenInfo* results);
   void UpdateCursor(const WebCursor& cursor);
-  void TransformPointToRootCoordSpace(const gfx::Point& point,
-                                      cc::SurfaceId surface_id,
-                                      gfx::Point* transformed_point);
+  gfx::Point TransformPointToRootCoordSpace(const gfx::Point& point,
+                                            cc::SurfaceId surface_id);
 
   // Determines whether the root RenderWidgetHostView (and thus the current
   // page) has focus.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b63e42f..4759476 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1327,9 +1327,9 @@
   // It is necessary to transform the coordinates to account for nested
   // RenderWidgetHosts, such as with out-of-process iframes.
   gfx::Point original_point(validated_params.x, validated_params.y);
-  gfx::Point transformed_point = original_point;
-  static_cast<RenderWidgetHostViewBase*>(GetView())
-      ->TransformPointToRootCoordSpace(original_point, &transformed_point);
+  gfx::Point transformed_point =
+      static_cast<RenderWidgetHostViewBase*>(GetView())
+          ->TransformPointToRootCoordSpace(original_point);
   validated_params.x = transformed_point.x();
   validated_params.y = transformed_point.y();
 
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index abd5d53..61c61a3 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -168,6 +168,7 @@
   void InsertVisualStateCallback(
       const VisualStateCallback& callback) override;
   bool IsRenderFrameLive() override;
+  RenderWidgetHostView* GetView() override;
 #if defined(OS_ANDROID)
   void ActivateNearestFindResult(int request_id, float x, float y) override;
   void RequestFindMatchRects(int current_version) override;
@@ -251,9 +252,6 @@
   // pointer to the RenderViewHost (which inherits RenderWidgetHost).
   RenderWidgetHostImpl* GetRenderWidgetHost();
 
-  // This returns the RenderWidgetHostView that can be used to control
-  // focus and visibility for this frame.
-  RenderWidgetHostView* GetView();
 
   // This function is called when this is a swapped out RenderFrameHost that
   // lives in the same process as the parent frame. The
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index 98119d15b..10a0681 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -381,15 +381,12 @@
     host_->ForwardWheelEvent(event);
 }
 
-void RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpace(
-    const gfx::Point& point,
-    gfx::Point* transformed_point) {
-  *transformed_point = point;
+gfx::Point RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpace(
+    const gfx::Point& point) {
   if (!frame_connector_ || !use_surfaces_)
-    return;
+    return point;
 
-  frame_connector_->TransformPointToRootCoordSpace(point, surface_id_,
-                                                   transformed_point);
+  return frame_connector_->TransformPointToRootCoordSpace(point, surface_id_);
 }
 
 #if defined(OS_MACOSX)
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.h b/content/browser/frame_host/render_widget_host_view_child_frame.h
index 4bacd21f3..c77b216 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.h
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.h
@@ -128,8 +128,7 @@
   void ProcessKeyboardEvent(const NativeWebKeyboardEvent& event) override;
   void ProcessMouseEvent(const blink::WebMouseEvent& event) override;
   void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
-  void TransformPointToRootCoordSpace(const gfx::Point& point,
-                                      gfx::Point* transformed_point) override;
+  gfx::Point TransformPointToRootCoordSpace(const gfx::Point& point) override;
 
 #if defined(OS_MACOSX)
   // RenderWidgetHostView implementation.
diff --git a/content/browser/media/android/browser_media_session_manager.cc b/content/browser/media/android/browser_media_session_manager.cc
index 146a60fe..a7e5fd5b 100644
--- a/content/browser/media/android/browser_media_session_manager.cc
+++ b/content/browser/media/android/browser_media_session_manager.cc
@@ -6,6 +6,8 @@
 
 #include "content/common/media/media_session_messages_android.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/media_metadata.h"
 
 namespace content {
 
@@ -23,6 +25,27 @@
   Send(new MediaSessionMsg_DidDeactivate(GetRoutingID(), request_id));
 }
 
+void BrowserMediaSessionManager::OnSetMetadata(
+    int session_id,
+    const MediaMetadata& insecure_metadata) {
+  // When receiving a MediaMetadata, the browser process can't trust that it is
+  // coming from a known and secure source. It must be processed accordingly.
+  MediaMetadata metadata;
+  metadata.title =
+      insecure_metadata.title.substr(0, MediaMetadata::kMaxIPCStringLength);
+  metadata.artist =
+      insecure_metadata.artist.substr(0, MediaMetadata::kMaxIPCStringLength);
+  metadata.album =
+      insecure_metadata.album.substr(0, MediaMetadata::kMaxIPCStringLength);
+
+  if (metadata != insecure_metadata) {
+    render_frame_host_->GetProcess()->ShutdownForBadMessage();
+    return;
+  }
+
+  NOTIMPLEMENTED();
+}
+
 int BrowserMediaSessionManager::GetRoutingID() const {
   return render_frame_host_->GetRoutingID();
 }
diff --git a/content/browser/media/android/browser_media_session_manager.h b/content/browser/media/android/browser_media_session_manager.h
index 3b07b295..b936ba0 100644
--- a/content/browser/media/android/browser_media_session_manager.h
+++ b/content/browser/media/android/browser_media_session_manager.h
@@ -14,6 +14,7 @@
 namespace content {
 
 class RenderFrameHost;
+struct MediaMetadata;
 
 class BrowserMediaSessionManager {
  public:
@@ -22,6 +23,7 @@
   // Message handlers.
   void OnActivate(int session_id, int request_id);
   void OnDeactivate(int session_id, int request_id);
+  void OnSetMetadata(int session_id, const MediaMetadata& metadata);
 
   int GetRoutingID() const;
 
diff --git a/content/browser/media/android/media_web_contents_observer_android.cc b/content/browser/media/android/media_web_contents_observer_android.cc
index 1b48959..224a8f1 100644
--- a/content/browser/media/android/media_web_contents_observer_android.cc
+++ b/content/browser/media/android/media_web_contents_observer_android.cc
@@ -96,7 +96,13 @@
   if (OnMediaPlayerMessageReceived(msg, render_frame_host))
     return true;
 
-  return OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host);
+  if (OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host))
+    return true;
+
+  if (OnMediaSessionMessageReceived(msg, render_frame_host))
+    return true;
+
+  return false;
 }
 
 bool MediaWebContentsObserverAndroid::OnMediaPlayerMessageReceived(
@@ -143,12 +149,6 @@
                         GetMediaPlayerManager(render_frame_host),
                         BrowserMediaPlayerManager::OnNotifyExternalSurface)
 #endif  // defined(VIDEO_HOLE)
-    IPC_MESSAGE_FORWARD(MediaSessionHostMsg_Activate,
-                        GetMediaSessionManager(render_frame_host),
-                        BrowserMediaSessionManager::OnActivate)
-    IPC_MESSAGE_FORWARD(MediaSessionHostMsg_Deactivate,
-                        GetMediaSessionManager(render_frame_host),
-                        BrowserMediaSessionManager::OnDeactivate)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -166,6 +166,27 @@
   return handled;
 }
 
+bool MediaWebContentsObserverAndroid::OnMediaSessionMessageReceived(
+    const IPC::Message& msg,
+    RenderFrameHost* render_frame_host) {
+  bool handled = true;
+
+  IPC_BEGIN_MESSAGE_MAP(MediaWebContentsObserver, msg)
+    IPC_MESSAGE_FORWARD(MediaSessionHostMsg_Activate,
+                        GetMediaSessionManager(render_frame_host),
+                        BrowserMediaSessionManager::OnActivate)
+    IPC_MESSAGE_FORWARD(MediaSessionHostMsg_Deactivate,
+                        GetMediaSessionManager(render_frame_host),
+                        BrowserMediaSessionManager::OnDeactivate)
+    IPC_MESSAGE_FORWARD(MediaSessionHostMsg_SetMetadata,
+                        GetMediaSessionManager(render_frame_host),
+                        BrowserMediaSessionManager::OnSetMetadata)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+
 void MediaWebContentsObserverAndroid::OnSetCdm(
     RenderFrameHost* render_frame_host,
     int player_id,
diff --git a/content/browser/media/android/media_web_contents_observer_android.h b/content/browser/media/android/media_web_contents_observer_android.h
index a95e161..5a6f481 100644
--- a/content/browser/media/android/media_web_contents_observer_android.h
+++ b/content/browser/media/android/media_web_contents_observer_android.h
@@ -56,6 +56,9 @@
   bool OnMediaPlayerSetCdmMessageReceived(const IPC::Message& message,
                                           RenderFrameHost* render_frame_host);
 
+  bool OnMediaSessionMessageReceived(const IPC::Message& message,
+                                     RenderFrameHost* render_frame_host);
+
   void OnSetCdm(RenderFrameHost* render_frame_host, int player_id, int cdm_id);
 
   // Map from RenderFrameHost* to BrowserMediaPlayerManager.
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index a2a0884..3bd1ffc 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -14,6 +14,7 @@
 #include "content/common/content_switches_internal.h"
 #include "content/public/browser/render_widget_host_view_frame_subscriber.h"
 #include "ui/gfx/display.h"
+#include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/screen.h"
@@ -682,10 +683,15 @@
   return 0;
 }
 
-void RenderWidgetHostViewBase::TransformPointToRootCoordSpace(
-    const gfx::Point& point,
-    gfx::Point* transformed_point) {
-  *transformed_point = point;
+gfx::Point RenderWidgetHostViewBase::TransformPointToRootCoordSpace(
+    const gfx::Point& point) {
+  return point;
+}
+
+gfx::PointF RenderWidgetHostViewBase::TransformPointToRootCoordSpaceF(
+    const gfx::PointF& point) {
+  return gfx::PointF(TransformPointToRootCoordSpace(
+      gfx::ToRoundedPoint(point)));
 }
 
 void RenderWidgetHostViewBase::TransformPointToLocalCoordSpace(
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index d714137..fa223b8 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -88,6 +88,12 @@
       scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) override;
   void EndFrameSubscription() override;
 
+  // This only needs to be overridden by RenderWidgetHostViewBase subclasses
+  // that handle content embedded within other RenderWidgetHostViews.
+  gfx::Point TransformPointToRootCoordSpace(const gfx::Point& point) override;
+  gfx::PointF TransformPointToRootCoordSpaceF(
+      const gfx::PointF& point) override;
+
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& msg) override;
 
@@ -204,15 +210,6 @@
   virtual void ProcessTouchEvent(const blink::WebTouchEvent& event,
                          const ui::LatencyInfo& latency) {}
 
-  // If a RenderWidgetHost is dealing with points that are transformed from the
-  // root frame for a page (i.e. because its content is contained within
-  // that of another RenderWidgetHost), this provides a facility to convert
-  // a point from its own coordinate space to that of the root frame.
-  // This only needs to be overriden by RenderWidgetHostView subclasses
-  // that handle content embedded within other RenderWidgetHostViews.
-  virtual void TransformPointToRootCoordSpace(const gfx::Point& point,
-                                              gfx::Point* transformed_point);
-
   // Transform a point that is in the coordinate space of a Surface that is
   // embedded within the RenderWidgetHostViewBase's Surface to the
   // coordinate space of the embedding Surface. Typically this means that a
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index d427d92..67318be 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -602,6 +602,20 @@
   }
 };
 
+// SitePerProcessIgnoreCertErrorsBrowserTest
+
+class SitePerProcessIgnoreCertErrorsBrowserTest
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessIgnoreCertErrorsBrowserTest() {}
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SitePerProcessBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+  }
+};
+
 // Ensure that navigating subframes in --site-per-process mode works and the
 // correct documents are committed.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
@@ -4984,4 +4998,104 @@
   EXPECT_EQ(b_url.GetOrigin().spec(), popup_origin + "/");
 }
 
+// Tests that the WebContents is notified when passive mixed content is
+// displayed in an OOPIF. The test ignores cert errors so that an HTTPS
+// iframe can be loaded from a site other than localhost (the
+// EmbeddedTestServer serves a certificate that is valid for localhost).
+IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       PassiveMixedContentInIframe) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(https_server.Start());
+  SetupCrossSiteRedirector(&https_server);
+
+  GURL iframe_url(
+      https_server.GetURL("/mixed-content/basic-passive-in-iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
+  EXPECT_TRUE(shell()->web_contents()->DisplayedInsecureContent());
+
+  // When the subframe navigates, the WebContents should still be marked
+  // as having displayed insecure content.
+  GURL navigate_url(https_server.GetURL("/title1.html"));
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  NavigateFrameToURL(root->child_at(0), navigate_url);
+  EXPECT_TRUE(shell()->web_contents()->DisplayedInsecureContent());
+
+  // When the main frame navigates, it should no longer be marked as
+  // displaying insecure content.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
+  EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
+}
+
+// Tests that, when a parent frame is set to strictly block mixed
+// content via Content Security Policy, child OOPIFs cannot display
+// mixed content.
+IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       PassiveMixedContentInIframeWithStrictBlocking) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(https_server.Start());
+  SetupCrossSiteRedirector(&https_server);
+
+  GURL iframe_url_with_strict_blocking(https_server.GetURL(
+      "/mixed-content/basic-passive-in-iframe-with-strict-blocking.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_strict_blocking));
+  EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  EXPECT_TRUE(root->current_replication_state()
+                  .should_enforce_strict_mixed_content_checking);
+  EXPECT_TRUE(root->child_at(0)
+                  ->current_replication_state()
+                  .should_enforce_strict_mixed_content_checking);
+
+  // When the subframe navigates, it should still be marked as enforcing
+  // strict mixed content.
+  GURL navigate_url(https_server.GetURL("/title1.html"));
+  NavigateFrameToURL(root->child_at(0), navigate_url);
+  EXPECT_TRUE(root->current_replication_state()
+                  .should_enforce_strict_mixed_content_checking);
+  EXPECT_TRUE(root->child_at(0)
+                  ->current_replication_state()
+                  .should_enforce_strict_mixed_content_checking);
+
+  // When the main frame navigates, it should no longer be marked as
+  // enforcing strict mixed content.
+  EXPECT_TRUE(
+      NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
+  EXPECT_FALSE(root->current_replication_state()
+                   .should_enforce_strict_mixed_content_checking);
+}
+
+// Tests that active mixed content is blocked in an OOPIF. The test
+// ignores cert errors so that an HTTPS iframe can be loaded from a site
+// other than localhost (the EmbeddedTestServer serves a certificate
+// that is valid for localhost).
+IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
+                       ActiveMixedContentInIframe) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(https_server.Start());
+  SetupCrossSiteRedirector(&https_server);
+
+  GURL iframe_url(
+      https_server.GetURL("/mixed-content/basic-active-in-iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* mixed_child = root->child_at(0)->child_at(0);
+  ASSERT_TRUE(mixed_child);
+  // The child iframe attempted to create a mixed iframe; this should
+  // have been blocked, so the mixed iframe should not have committed a
+  // load.
+  EXPECT_FALSE(mixed_child->has_committed_real_load());
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 70edf234..59e1dbd 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -12,6 +12,7 @@
 #include "base/sys_info.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "components/tracing/process_metrics_memory_dump_provider.h"
 #include "content/browser/tracing/file_tracing_provider_impl.h"
 #include "content/browser/tracing/power_tracing_agent.h"
 #include "content/browser/tracing/trace_message_filter.h"
@@ -557,6 +558,13 @@
     return;
   }
 
+#if defined(OS_LINUX)
+  // On Linux the browser process dumps process metrics for child process due to
+  // sandbox.
+  tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
+      trace_message_filter->peer_pid());
+#endif
+
   trace_message_filters_.insert(trace_message_filter);
   if (can_cancel_watch_event()) {
     trace_message_filter->SendSetWatchEvent(watch_category_name_,
@@ -585,6 +593,11 @@
     return;
   }
 
+#if defined(OS_LINUX)
+  tracing::ProcessMetricsMemoryDumpProvider::UnregisterForProcess(
+      trace_message_filter->peer_pid());
+#endif
+
   // If a filter is removed while a response from that filter is pending then
   // simulate the response. Otherwise the response count will be wrong and the
   // completion callback will never be executed.
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 95e23f0..510b0fe 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -375,6 +375,8 @@
   IPC_STRUCT_TRAITS_MEMBER(origin)
   IPC_STRUCT_TRAITS_MEMBER(sandbox_flags)
   IPC_STRUCT_TRAITS_MEMBER(name)
+  IPC_STRUCT_TRAITS_MEMBER(scope)
+  IPC_STRUCT_TRAITS_MEMBER(should_enforce_strict_mixed_content_checking)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_BEGIN(FrameMsg_NewFrame_WidgetParams)
diff --git a/content/common/media/media_session_messages_android.h b/content/common/media/media_session_messages_android.h
index 18a63844..266ee27 100644
--- a/content/common/media/media_session_messages_android.h
+++ b/content/common/media/media_session_messages_android.h
@@ -7,12 +7,19 @@
 
 #include "content/common/android/gin_java_bridge_errors.h"
 #include "content/common/content_export.h"
+#include "content/public/common/media_metadata.h"
 #include "ipc/ipc_message_macros.h"
 
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 #define IPC_MESSAGE_START MediaSessionMsgStart
 
+IPC_STRUCT_TRAITS_BEGIN(content::MediaMetadata)
+  IPC_STRUCT_TRAITS_MEMBER(title)
+  IPC_STRUCT_TRAITS_MEMBER(artist)
+  IPC_STRUCT_TRAITS_MEMBER(album)
+IPC_STRUCT_TRAITS_END()
+
 // Messages for notifying the render process of media session status -------
 
 IPC_MESSAGE_ROUTED2(MediaSessionMsg_DidActivate,
@@ -30,3 +37,7 @@
 IPC_MESSAGE_ROUTED2(MediaSessionHostMsg_Deactivate,
                     int /* session_id */,
                     int /* request_id */)
+
+IPC_MESSAGE_ROUTED2(MediaSessionHostMsg_SetMetadata,
+                    int /* request_id*/,
+                    content::MediaMetadata /* metadata */)
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 1231847..5bcd2a8 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -87,6 +87,8 @@
       'public/common/main_function_params.h',
       'public/common/manifest.cc',
       'public/common/manifest.h',
+      'public/common/media_metadata.cc',
+      'public/common/media_metadata.h',
       'public/common/media_stream_request.cc',
       'public/common/media_stream_request.h',
       'public/common/menu_item.cc',
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index a8f71fe3..a2e497e7 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -26,6 +26,7 @@
 namespace content {
 class RenderProcessHost;
 class RenderViewHost;
+class RenderWidgetHostView;
 class ServiceRegistry;
 class SiteInstance;
 
@@ -73,6 +74,10 @@
   // Returns the process for this frame.
   virtual RenderProcessHost* GetProcess() = 0;
 
+  // Returns the RenderWidgetHostView that can be used to control focus and
+  // visibility for this frame.
+  virtual RenderWidgetHostView* GetView() = 0;
+
   // Returns the current RenderFrameHost of the parent frame, or nullptr if
   // there is no parent. The result may be in a different process than the
   // current RenderFrameHost.
diff --git a/content/public/browser/render_widget_host_view.h b/content/public/browser/render_widget_host_view.h
index 9b48ddb..7c749a2 100644
--- a/content/public/browser/render_widget_host_view.h
+++ b/content/public/browser/render_widget_host_view.h
@@ -65,6 +65,19 @@
   // Retrieves the last known scroll position.
   virtual gfx::Vector2dF GetLastScrollOffset() const = 0;
 
+  // Coordinate points received from a renderer process need to be transformed
+  // to the top-level frame's coordinate space. For coordinates received from
+  // the top-level frame's renderer this is a no-op as they are already
+  // properly transformed; however, coordinates received from an out-of-process
+  // iframe renderer process require transformation.
+  virtual gfx::Point TransformPointToRootCoordSpace(
+      const gfx::Point& point) = 0;
+
+  // A floating point variant of the above. PointF values will be snapped to
+  // integral points before transformation.
+  virtual gfx::PointF TransformPointToRootCoordSpaceF(
+      const gfx::PointF& point) = 0;
+
   // Retrieves the native view used to contain plugins and identify the
   // renderer in IPC messages.
   virtual gfx::NativeView GetNativeView() const = 0;
diff --git a/content/public/common/media_metadata.cc b/content/public/common/media_metadata.cc
new file mode 100644
index 0000000..dc3824c2
--- /dev/null
+++ b/content/public/common/media_metadata.cc
@@ -0,0 +1,23 @@
+// 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.
+
+#include "content/public/common/media_metadata.h"
+
+namespace content {
+
+const size_t MediaMetadata::kMaxIPCStringLength = 4 * 1024;
+
+MediaMetadata::MediaMetadata() = default;
+
+MediaMetadata::~MediaMetadata() = default;
+
+bool MediaMetadata::operator==(const MediaMetadata& other) const {
+  return title == other.title && artist == other.artist && album == other.album;
+}
+
+bool MediaMetadata::operator!=(const MediaMetadata& other) const {
+  return !this->operator==(other);
+}
+
+}  // namespace content
diff --git a/content/public/common/media_metadata.h b/content/public/common/media_metadata.h
new file mode 100644
index 0000000..3d7f13c
--- /dev/null
+++ b/content/public/common/media_metadata.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CONTENT_PUBLIC_COMMON_MEDIA_METADATA_H_
+#define CONTENT_PUBLIC_COMMON_MEDIA_METADATA_H_
+
+#include "base/strings/string16.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// The MediaMetadata is a structure carrying information associated to a
+// content::MediaSession.
+struct CONTENT_EXPORT MediaMetadata {
+  MediaMetadata();
+  ~MediaMetadata();
+
+  bool operator==(const MediaMetadata& other) const;
+  bool operator!=(const MediaMetadata& other) const;
+
+  // Title associated to the MediaSession.
+  base::string16 title;
+
+  // Artist associated to the MediaSession.
+  base::string16 artist;
+
+  // Album associated to the MediaSession.
+  base::string16 album;
+
+  // Maximum length for all the strings inside the MediaMetadata when it is sent
+  // over IPC. The renderer process should truncate the strings before sending
+  // the MediaMetadata and the browser process must do the same when receiving
+  // it.
+  static const size_t kMaxIPCStringLength;
+};
+
+}  // namespace content
+
+#endif // CONTENT_PUBLIC_COMMON_MEDIA_METADATA_H_
diff --git a/content/renderer/media/android/renderer_media_session_manager.cc b/content/renderer/media/android/renderer_media_session_manager.cc
index 58b0888..2670a4a38 100644
--- a/content/renderer/media/android/renderer_media_session_manager.cc
+++ b/content/renderer/media/android/renderer_media_session_manager.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "content/common/media/media_session_messages_android.h"
+#include "content/public/common/media_metadata.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/renderer/media/android/webmediasession_android.h"
 
@@ -59,6 +60,23 @@
       new MediaSessionHostMsg_Deactivate(routing_id(), session_id, request_id));
 }
 
+void RendererMediaSessionManager::SetMetadata(
+    int session_id,
+    const MediaMetadata& metadata) {
+  // Apply some sanity checks on the MediaMetadata before sending over IPC.
+  MediaMetadata ipc_metadata;
+  ipc_metadata.title =
+      metadata.title.substr(0, MediaMetadata::kMaxIPCStringLength);
+  ipc_metadata.artist =
+      metadata.artist.substr(0, MediaMetadata::kMaxIPCStringLength);
+  ipc_metadata.album =
+      metadata.album.substr(0, MediaMetadata::kMaxIPCStringLength);
+
+  Send(new MediaSessionHostMsg_SetMetadata(routing_id(),
+                                           session_id,
+                                           ipc_metadata));
+}
+
 void RendererMediaSessionManager::OnDidActivate(int request_id, bool success) {
   DCHECK(pending_activation_requests_.Lookup(request_id)) << request_id;
   blink::WebMediaSessionActivateCallback* callback =
diff --git a/content/renderer/media/android/renderer_media_session_manager.h b/content/renderer/media/android/renderer_media_session_manager.h
index c7f75044..5c5a2f2 100644
--- a/content/renderer/media/android/renderer_media_session_manager.h
+++ b/content/renderer/media/android/renderer_media_session_manager.h
@@ -16,6 +16,7 @@
 namespace content {
 
 class WebMediaSessionAndroid;
+struct MediaMetadata;
 
 class CONTENT_EXPORT RendererMediaSessionManager : public RenderFrameObserver {
  public:
@@ -33,6 +34,7 @@
   void Deactivate(
       int session_id,
       scoped_ptr<blink::WebMediaSessionDeactivateCallback> callback);
+  void SetMetadata(int session_id, const MediaMetadata& metadata);
 
   void OnDidActivate(int request_id, bool success);
   void OnDidDeactivate(int request_id);
diff --git a/content/renderer/media/android/webmediasession_android.cc b/content/renderer/media/android/webmediasession_android.cc
index b10d5f26..ddfa13c7 100644
--- a/content/renderer/media/android/webmediasession_android.cc
+++ b/content/renderer/media/android/webmediasession_android.cc
@@ -6,7 +6,9 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "content/public/common/media_metadata.h"
 #include "content/renderer/media/android/renderer_media_session_manager.h"
+#include "third_party/WebKit/public/platform/modules/mediasession/WebMediaMetadata.h"
 
 namespace content {
 
@@ -32,8 +34,15 @@
 }
 
 void WebMediaSessionAndroid::setMetadata(
-    const blink::WebMediaMetadata* metadata) {
-  NOTIMPLEMENTED();
+    const blink::WebMediaMetadata* web_metadata) {
+  MediaMetadata metadata;
+  if (web_metadata) {
+    metadata.title = web_metadata->title;
+    metadata.artist = web_metadata->artist;
+    metadata.album = web_metadata->album;
+  }
+
+  session_manager_->SetMetadata(session_id_, metadata);
 }
 
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index ad0e0ab..75e77b4 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2423,7 +2423,8 @@
     WebMediaPlayerClient* client,
     WebMediaPlayerEncryptedMediaClient* encrypted_client,
     WebContentDecryptionModule* initial_cdm,
-    const blink::WebString& sink_id) {
+    const blink::WebString& sink_id,
+    WebMediaSession* media_session) {
 #if defined(VIDEO_HOLE)
   if (!contains_media_player_) {
     render_view_->RegisterVideoHoleFrame(this);
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index cd1f5987..f9cf06c 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -419,7 +419,8 @@
       blink::WebMediaPlayerClient* client,
       blink::WebMediaPlayerEncryptedMediaClient* encrypted_client,
       blink::WebContentDecryptionModule* initial_cdm,
-      const blink::WebString& sink_id) override;
+      const blink::WebString& sink_id,
+      blink::WebMediaSession* media_session) override;
   blink::WebMediaSession* createMediaSession() override;
   blink::WebApplicationCacheHost* createApplicationCacheHost(
       blink::WebLocalFrame* frame,
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc
index 2e1e4ff..a16fdc00 100644
--- a/content/shell/browser/shell_views.cc
+++ b/content/shell/browser/shell_views.cc
@@ -54,23 +54,6 @@
 namespace content {
 
 namespace {
-// ViewDelegate implementation for aura content shell
-class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate {
- public:
-  ShellViewsDelegateAura() : use_transparent_windows_(false) {
-  }
-
-  ~ShellViewsDelegateAura() override {}
-
-  void SetUseTransparentWindows(bool transparent) {
-    use_transparent_windows_ = transparent;
-  }
-
- private:
-  bool use_transparent_windows_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura);
-};
 
 // Model for the "Debug" menu
 class ContextMenuModel : public ui::SimpleMenuModel,
@@ -421,7 +404,7 @@
   gfx::Screen::SetScreenInstance(
       gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen());
 #endif
-  views_delegate_ = new ShellViewsDelegateAura();
+  views_delegate_ = new views::DesktopTestViewsDelegate();
 }
 
 void Shell::PlatformExit() {
diff --git a/content/test/data/mixed-content/basic-active-in-iframe.html b/content/test/data/mixed-content/basic-active-in-iframe.html
new file mode 100644
index 0000000..9e2438f
--- /dev/null
+++ b/content/test/data/mixed-content/basic-active-in-iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body>
+</body>
+<iframe src="/cross-site/a.com/mixed-content/basic-active.html"></iframe>
+</html>
diff --git a/content/test/data/mixed-content/basic-active.html b/content/test/data/mixed-content/basic-active.html
new file mode 100644
index 0000000..96ec3b3
--- /dev/null
+++ b/content/test/data/mixed-content/basic-active.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+	<iframe src="http://a.test"></iframe>
+</body>
+</html>
diff --git a/content/test/data/mixed-content/basic-passive-in-iframe-with-strict-blocking.html b/content/test/data/mixed-content/basic-passive-in-iframe-with-strict-blocking.html
new file mode 100644
index 0000000..5ef54ce
--- /dev/null
+++ b/content/test/data/mixed-content/basic-passive-in-iframe-with-strict-blocking.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
+<body>
+  <iframe src="/cross-site/a.com/mixed-content/basic-passive.html"></iframe>
+</body>
+</html>
diff --git a/content/test/data/mixed-content/basic-passive-in-iframe.html b/content/test/data/mixed-content/basic-passive-in-iframe.html
new file mode 100644
index 0000000..5d10e96
--- /dev/null
+++ b/content/test/data/mixed-content/basic-passive-in-iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body>
+  <iframe src="/cross-site/a.com/mixed-content/basic-passive.html"></iframe>
+</body>
+</html>
diff --git a/content/test/data/mixed-content/basic-passive.html b/content/test/data/mixed-content/basic-passive.html
new file mode 100644
index 0000000..b639b55f
--- /dev/null
+++ b/content/test/data/mixed-content/basic-passive.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body>
+This page tries to load some passive mixed content.
+<img src="http://a.test/image.jpg" />
+</body>
+</html>
diff --git a/docs/updating_clang_format_binaries.md b/docs/updating_clang_format_binaries.md
index 8ba14ae3..0e10f49 100644
--- a/docs/updating_clang_format_binaries.md
+++ b/docs/updating_clang_format_binaries.md
@@ -76,8 +76,6 @@
 -DCMAKE_C_COMPILER=$PWD/../chrome/src/third_party/llvm-build/Release+Asserts/bin/clang -DCMAKE_CXX_COMPILER=$PWD/../chrome/src/third_party/llvm-build/Release+Asserts/bin/clang++
 ```
 
-TODO: these ^^ instructions looks odd. Are they correct???
-
 Platform specific notes:
 
 *   Windows: Visual Studio 2013 only.
diff --git a/docs/writing_clang_plugins.md b/docs/writing_clang_plugins.md
index b8a65f6..4ca801a0 100644
--- a/docs/writing_clang_plugins.md
+++ b/docs/writing_clang_plugins.md
@@ -2,11 +2,6 @@
 
 [TOC]
 
-TODO: although cs.chromium.org
-[finds](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/llvm/)
-`src/third_party/llvm`, it
-[does not exist in Gitiles](https://chromium.googlesource.com/src/third_party/llvm/).
-
 Make sure you really want to write a clang plugin.
 
 *   The clang plugin api is not stable. If you write a plugin, _you_ are
diff --git a/extensions/renderer/module_system_test.cc b/extensions/renderer/module_system_test.cc
index 50de79fe..68c4bd1f 100644
--- a/extensions/renderer/module_system_test.cc
+++ b/extensions/renderer/module_system_test.cc
@@ -22,7 +22,6 @@
 #include "extensions/renderer/object_backed_native_handler.h"
 #include "extensions/renderer/safe_builtins.h"
 #include "extensions/renderer/utils_native_handler.h"
-#include "third_party/WebKit/public/web/WebHeap.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace extensions {
@@ -241,7 +240,6 @@
     old_heap_size = stats.used_heap_size();
     isolate_->RequestGarbageCollectionForTesting(
         v8::Isolate::kFullGarbageCollection);
-    blink::WebHeap::collectAllGarbageForTesting();
     isolate_->GetHeapStatistics(&stats);
   }
 }
diff --git a/ios/chrome/browser/experimental_flags.h b/ios/chrome/browser/experimental_flags.h
index 84c2cc2f..a89326487 100644
--- a/ios/chrome/browser/experimental_flags.h
+++ b/ios/chrome/browser/experimental_flags.h
@@ -53,6 +53,9 @@
 // Whether the Tab Switcher is enabled for iPad or not.
 bool IsTabSwitcherEnabled();
 
+// Whether the reading list is enabled.
+bool IsReadingListEnabled();
+
 }  // namespace experimental_flags
 
 #endif  // IOS_CHROME_BROWSER_EXPERIMENTAL_FLAGS_H_
diff --git a/ios/chrome/browser/experimental_flags.mm b/ios/chrome/browser/experimental_flags.mm
index b6c537c9..297172d 100644
--- a/ios/chrome/browser/experimental_flags.mm
+++ b/ios/chrome/browser/experimental_flags.mm
@@ -28,6 +28,7 @@
 NSString* const kHeuristicsForPasswordGeneration =
     @"HeuristicsForPasswordGeneration";
 const char* const kWKWebViewTrialName = "IOSUseWKWebView";
+NSString* const kEnableReadingList = @"EnableReadingList";
 
 enum class WKWebViewEligibility {
   // UNSET indicates that no explicit call to set eligibility has been made,
@@ -230,4 +231,8 @@
                           base::CompareCase::INSENSITIVE_ASCII);
 }
 
+bool IsReadingListEnabled() {
+  return [[NSUserDefaults standardUserDefaults] boolForKey:kEnableReadingList];
+}
+
 }  // namespace experimental_flags
diff --git a/mojo/public/interfaces/bindings/tests/test_native_types.mojom b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
index 1012756..e0767f3 100644
--- a/mojo/public/interfaces/bindings/tests/test_native_types.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
@@ -8,7 +8,7 @@
 
 // Used to verify that structs can be declared with no body in mojom.
 
-[native=True]
+[Native=True]
 struct PickledStruct;
 
 interface PicklePasser {
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
index 60ae1e0..e8c71f3 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/data.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -225,12 +225,12 @@
     struct.fields_data = data['fields']
   struct.attributes = data.get('attributes')
 
-  # Enforce that a [native=True] attribute is set to make native-only struct
+  # Enforce that a [Native=True] attribute is set to make native-only struct
   # declarations more explicit.
   if struct.native_only:
-    if not struct.attributes or not struct.attributes.get('native', False):
+    if not struct.attributes or not struct.attributes.get('Native', False):
       raise Exception("Native-only struct declarations must include a " +
-                      "native=True attribute.")
+                      "Native=True attribute.")
 
   return struct
 
diff --git a/net/net_unittests_apk.isolate b/net/net_unittests_apk.isolate
index b1e945b..07ef728 100644
--- a/net/net_unittests_apk.isolate
+++ b/net/net_unittests_apk.isolate
@@ -10,6 +10,7 @@
   'variables': {
     'command': [
      '<(PRODUCT_DIR)/bin/run_net_unittests',
+     '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats',
     ],
     'files': [
       '../base/base.isolate',
diff --git a/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java b/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java
index 7e8e205..aaea7aa 100644
--- a/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java
+++ b/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java
@@ -9,6 +9,7 @@
 import android.accounts.Account;
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.AsyncTask;
 import android.os.Process;
@@ -298,13 +299,16 @@
      */
     public void getAuthToken(final Account account, final String authTokenType,
             final GetAuthTokenCallback callback) {
-        ConnectionRetry.runAuthTask(new AuthTask<String>() {
+        ConnectionRetry.runAuthTask(mApplicationContext, new AuthTask<String>() {
+            @Override
             public String run() throws AuthException {
                 return mAccountManager.getAuthToken(account, authTokenType);
             }
+            @Override
             public void onSuccess(String token) {
                 callback.tokenAvailable(token);
             }
+            @Override
             public void onFailure(boolean isTransientError) {
                 callback.tokenUnavailable(isTransientError);
             }
@@ -334,12 +338,15 @@
         if (authToken == null || authToken.isEmpty()) {
             return;
         }
-        ConnectionRetry.runAuthTask(new AuthTask<Boolean>() {
+        ConnectionRetry.runAuthTask(mApplicationContext, new AuthTask<Boolean>() {
+            @Override
             public Boolean run() throws AuthException {
                 mAccountManager.invalidateAuthToken(authToken);
                 return true;
             }
+            @Override
             public void onSuccess(Boolean result) {}
+            @Override
             public void onFailure(boolean isTransientError) {
                 Log.e(TAG, "Failed to invalidate auth token: " + authToken);
             }
@@ -367,15 +374,17 @@
             implements NetworkChangeNotifier.ConnectionTypeObserver {
         private static final int MAX_TRIES = 3;
 
+        private final Context mContext;
         private final AuthTask<T> mAuthTask;
         private final AtomicInteger mNumTries;
         private final AtomicBoolean mIsTransientError;
 
-        public static <T> void runAuthTask(AuthTask<T> authTask) {
-            new ConnectionRetry<T>(authTask).attempt();
+        public static <T> void runAuthTask(Context context, AuthTask<T> authTask) {
+            new ConnectionRetry<T>(context, authTask).attempt();
         }
 
-        private ConnectionRetry(AuthTask<T> authTask) {
+        private ConnectionRetry(Context context, AuthTask<T> authTask) {
+            mContext = context;
             mAuthTask = authTask;
             mNumTries = new AtomicInteger(0);
             mIsTransientError = new AtomicBoolean(false);
@@ -394,9 +403,17 @@
                     try {
                         return mAuthTask.run();
                     } catch (AuthException ex) {
-                        // TODO(547048): Handle the recovery intent if it is present.
-                        Log.e(TAG, "Failed to perform auth task", ex);
+                        Log.w(TAG, "Failed to perform auth task", ex);
                         mIsTransientError.set(ex.isTransientError());
+
+                        // TODO(547048): This will fire the intent indiscriminately. We should fix
+                        // this in the future to fire only once per user actionable intent to avoid
+                        // spamming the user.
+                        if (ex.getRecoveryIntent() != null) {
+                            Intent i = ex.getRecoveryIntent();
+                            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                            mContext.startActivity(i);
+                        }
                     }
                     return null;
                 }
diff --git a/third_party/WebKit/LayoutTests/bluetooth/connectGATT.html b/third_party/WebKit/LayoutTests/bluetooth/connectGATT.html
index 55c695b..6cebf37 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/connectGATT.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/connectGATT.html
@@ -117,6 +117,30 @@
   }, testSpec.testName);
 });
 
+// TODO(ortuno): Allow connections when the tab is in the background.
+// This is a short term solution instead of implementing a tab indicator
+// for bluetooth connections.
+// https://crbug.com/579746
+promise_test(() => {
+  testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+  return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
+    .then(device => {
+      testRunner.setPageVisibility('hidden');
+      return assert_promise_rejects_with_message(
+        device.connectGATT(),
+        new DOMException('Connection is only allowed while the page is visible. ' +
+                         'This is a temporary measure until we are able to ' +
+                         'effectively communicate to the user that a page is ' +
+                         'connected to a device.',
+                         'SecurityError'));
+    })
+    .then(() => testRunner.setPageVisibility('visible'))
+    .catch(error => {
+      testRunner.setPageVisibility('visible');
+      throw error;
+    });
+}, 'Device should not be able to connect in background.');
+
 promise_test(() => {
   testRunner.setBluetoothMockDataSet('HeartRateAdapter');
   return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/detach-gc.html b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/detach-gc.html
new file mode 100644
index 0000000..28f2cdb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/detach-gc.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/bluetooth-helpers.js"></script>
+<body>
+  <script>
+  "use strict";
+  async_test(test => {
+    window.onmessage = messageEvent => test.step(() => {
+      if (messageEvent.data === 'Ready') {
+        let iframe = document.querySelector('iframe');
+        callWithKeyDown(() => {
+          iframe.contentWindow.postMessage('Go', '*');
+        });
+      } else if (messageEvent.data === 'Connected') {
+        // Detach
+        iframe.remove();
+        // GC
+        runGarbageCollection().then(() => test.done());
+      } else {
+        assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+      }
+    });
+    testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+    let iframe = document.createElement('iframe');
+    iframe.src = '../resources/connectGATT-iframe.html';
+    document.body.appendChild(iframe);
+  }, 'Detach frame then garbage collect. We shouldn\'t crash.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/gc-detach.html b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/gc-detach.html
new file mode 100644
index 0000000..e5b4fda
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/gc-detach.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/bluetooth-helpers.js"></script>
+<body>
+  <script>
+  "use strict";
+  async_test(test => {
+    window.onmessage = messageEvent => test.step(() => {
+      if (messageEvent.data === 'Ready') {
+        let iframe = document.querySelector('iframe');
+        callWithKeyDown(() => {
+          iframe.contentWindow.postMessage('Go', '*');
+        });
+      } else if (messageEvent.data === 'Connected') {
+        // GC
+        runGarbageCollection().then(() => {
+          // Detach
+          iframe.remove();
+          test.done();
+        });
+      } else {
+        assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+      }
+    });
+    testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+    let iframe = document.createElement('iframe');
+    iframe.src = '../resources/connectGATT-iframe.html';
+    document.body.appendChild(iframe);
+  }, 'Garbage collect then detach frame. We shouldn\'t crash.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/hide-detach.html b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/hide-detach.html
new file mode 100644
index 0000000..59bff78
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/disconnect-frame-detached/hide-detach.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/bluetooth-helpers.js"></script>
+<body>
+  <script>
+  "use strict";
+  async_test(test => {
+    window.onmessage = messageEvent => test.step(() => {
+      if (messageEvent.data === 'Ready') {
+        let iframe = document.querySelector('iframe');
+        callWithKeyDown(() => {
+          iframe.contentWindow.postMessage('Go', '*');
+        });
+      } else if (messageEvent.data === 'Connected') {
+        // Hide
+        testRunner.setPageVisibility('hidden');
+        // Detach
+        iframe.remove();
+        test.done();
+      } else {
+        assert_unreached('iframe sent invalid data: ' + messageEvent.data);
+      }
+    });
+    testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+    let iframe = document.createElement('iframe');
+    iframe.src = '../resources/connectGATT-iframe.html';
+    document.body.appendChild(iframe);
+  }, 'Hide then detach frame. We shouldn\'t crash.');
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/disconnect-when-hidden-or-closed.html b/third_party/WebKit/LayoutTests/bluetooth/disconnect-when-hidden-or-closed.html
new file mode 100644
index 0000000..fcf3ca0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/disconnect-when-hidden-or-closed.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="resources/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+test(t => { assert_true(window.testRunner instanceof Object); t.done(); },
+     'window.testRunner is required for the following tests.');
+
+// TODO(ortuno): Allow connections when the tab is in the background.
+// This is a short term solution instead of implementing a tab indicator
+// for bluetooth connections.
+// https://crbug.com/579746
+promise_test(() => {
+  testRunner.setPageVisibility("visible");
+  testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+  return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
+   .then(device => device.connectGATT())
+   .then(gatt_server => {
+     assert_true(gatt_server.connected);
+     testRunner.setPageVisibility("hidden");
+     assert_false(gatt_server.connected);
+     testRunner.setPageVisibility("visible");
+     assert_false(gatt_server.connected);
+   });
+}, 'Test device disconnects when tab becomes hidden');
+
+promise_test(() => {
+  testRunner.setPageVisibility('visible');
+  testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+  return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
+   .then(device => device.connectGATT())
+   .then(gatt_server => {})
+   .then(() => runGarbageCollection())
+   .then(() => testRunner.setPageVisibility('hidden'));
+}, 'Test object gets garbage collected before tab becomes hidden. ' +
+   'We shouldn\'t crash.');
+
+promise_test(() => {
+  testRunner.setPageVisibility('visible');
+  testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+  return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
+   .then(device => device.connectGATT())
+   .then(gatt_server => testRunner.setPageVisibility('hidden'))
+   .then(() => runGarbageCollection());
+}, 'Test object gets garbage collected after tab becomes hidden. ' +
+   'We shouldn\'t crash.');
+
+promise_test(() => {
+  testRunner.setPageVisibility('visible');
+  testRunner.setBluetoothMockDataSet('HeartRateAdapter');
+  return requestDeviceWithKeyDown({filters: [{services: ['heart_rate']}]})
+   .then(device => {
+     let connect_promise = device.connectGATT();
+     testRunner.setPageVisibility('hidden');
+     return connect_promise;
+   }).then(gatt_server => {
+     assert_false(gatt_server.connected);
+   });
+}, 'Visibility changes during connection. Should disconnect.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/resources/connectGATT-iframe.html b/third_party/WebKit/LayoutTests/bluetooth/resources/connectGATT-iframe.html
new file mode 100644
index 0000000..7c5fa721
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/resources/connectGATT-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script>
+  window.onmessage = messageEvent => {
+    if (messageEvent.data === 'Go') {
+      navigator.bluetooth.requestDevice({
+        filters: [{services: ['heart_rate']}]
+      })
+      .then(device => device.connectGATT())
+      .then(gattServer => {
+        parent.postMessage('Connected', '*');
+      }).catch(err => {
+        console.error(err);
+        parent.postMessage('FAIL: ' + err, '*');
+      });
+    }
+  };
+  parent.postMessage("Ready", "*");
+</script>
diff --git a/third_party/WebKit/LayoutTests/css-parser/ascii-case-insensitive-matching.html b/third_party/WebKit/LayoutTests/css-parser/ascii-case-insensitive-matching.html
new file mode 100644
index 0000000..fcca0108
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css-parser/ascii-case-insensitive-matching.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<style>
+#a {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+@support\017f (background-color: red) {
+  #a {
+    background-color: red;
+  }
+}
+</style>
+<div id=a></div>
+<script>
+test(function() {
+  assert_equals(getComputedStyle(a).backgroundColor, "rgb(0, 128, 0)");
+}, "Identifier values are matched ASCII case-insensitively");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-allowed.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-allowed.html
new file mode 100644
index 0000000..e71fd0b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-allowed.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="connect-src http://127.0.0.1:8000; script-src http://127.0.0.1:8000 'unsafe-inline'">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel=preload href="../../resources/dummy.js" id=preload>
+<script>
+    var t = async_test('Makes sure that connect-src allows link preload resources with no as type');
+    var preload = document.getElementById("preload");
+    preload.onload = t.step(function(){
+        t.done();
+    });
+</script>
+</head>
+<body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-blocked.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-blocked.html
new file mode 100644
index 0000000..efd3cf0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/connect-src-preload-blocked.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Security-Policy" content="connect-src http://foobar.test:8000; script-src http://127.0.0.1:8000 'unsafe-inline'">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel=preload href="../../resources/dummy.js" id=preload>
+<script>
+    var t = async_test('Makes sure that connect-src allows link preload resources with no as type');
+    var preload = document.getElementById("preload");
+    preload.onerror = t.step(function(){
+        t.done();
+    });
+</script>
+</head>
+<body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/active-subresource-in-iframe-blocked.https-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/active-subresource-in-iframe-blocked.https-expected.txt
index 00de7b8..109e3adc 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/active-subresource-in-iframe-blocked.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/active-subresource-in-iframe-blocked.https-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 4: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-css.html' was loaded over HTTPS, but requested an insecure stylesheet 'http://127.0.0.1:8080/security/mixedContent/resources/style.css'. This request has been blocked; the content must be served over HTTPS.
+CONSOLE ERROR: line 4: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/active-subresource-in-iframe-blocked.https.html' was loaded over HTTPS, but requested an insecure stylesheet 'http://127.0.0.1:8080/security/mixedContent/resources/style.css'. This request has been blocked; the content must be served over HTTPS.
 This test passes if the active subresource in the frame below is blocked.
 
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-css-image-with-reload-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-css-image-with-reload-expected.txt
index 488843b..38d63e8e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-css-image-with-reload-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-css-image-with-reload-expected.txt
@@ -1,5 +1,5 @@
-CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-css-image.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-subframe-and-reload.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This content should also be served over HTTPS.
 CONSOLE MESSAGE: line 12: Reloading...
-CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-css-image.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-subframe-and-reload.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This content should also be served over HTTPS.
 CONSOLE MESSAGE: line 17: Second load finished.
 This test opens a window that loads a frame with a subframe which contains insecure css image. The subframe is refreshed after it loads. We should trigger a mixed content callback twice despite the fact that on a second load of the subframe a css cache might be used to fetch an image.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-frame-in-data-iframe-in-main-frame-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-frame-in-data-iframe-in-main-frame-blocked-expected.txt
index d4979374b..bf3d5473 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-frame-in-data-iframe-in-main-frame-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-frame-in-data-iframe-in-main-frame-blocked-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE ERROR: line 1: Mixed Content: The page at 'data:text/html,<html><iframe src='http://127.0.0.1:8080/security/mixedContent/resources/boring.html'></iframe></html>' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This request has been blocked; the content must be served over HTTPS.
+CONSOLE ERROR: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-data-url-frame-with-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This request has been blocked; the content must be served over HTTPS.
 This test opens a window that loads a data: iframe that loads an insecure frame, and that the frame is still blocked.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-iframe-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-iframe-expected.txt
index f21296cc..c4d88439 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-iframe-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 8: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
 This test loads a secure iframe that loads an insecure iframe. We should get a mixed content callback becase the child frame is HTTPS.
 
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-allowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-allowed-expected.txt
index 062e4398..55e67f1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-allowed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-allowed-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE WARNING: line 9: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-iframe.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-iframe.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
 This test opens a window that loads an insecure iframe. We should trigger a mixed content callback even though we've set the preference to block this, because we've overriden the preference via a web permission client callback.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-blocked-expected.txt
index 0707e62..2d38a9b5 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-blocked-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE ERROR: line 9: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-iframe.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This request has been blocked; the content must be served over HTTPS.
+CONSOLE ERROR: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-iframe.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This request has been blocked; the content must be served over HTTPS.
 This test opens a window that loads an insecure iframe. We should not trigger a mixed content callback even though the main frame in the window is HTTPS and is displaying insecure content, because we've set the preference to block this.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-expected.txt
index 00ba869..b270262 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-iframe-in-main-frame-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 8: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
 This test opens a window that loads an insecure iframe. We should trigger a mixed content callback because the main frame in the window is HTTPS but is running insecure content.
 
 ============== Back Forward List ==============
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-script-in-data-iframe-in-main-frame-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-script-in-data-iframe-in-main-frame-blocked-expected.txt
index a5965de..61dcf77b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-script-in-data-iframe-in-main-frame-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/insecure-script-in-data-iframe-in-main-frame-blocked-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE ERROR: Mixed Content: The page at 'data:text/html,<html><script src='http://127.0.0.1:8080/security/mixedContent/resources/script.js'></script></html>' was loaded over HTTPS, but requested an insecure script 'http://127.0.0.1:8080/security/mixedContent/resources/script.js'. This request has been blocked; the content must be served over HTTPS.
+CONSOLE ERROR: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-data-url-frame-with-script.html' was loaded over HTTPS, but requested an insecure script 'http://127.0.0.1:8080/security/mixedContent/resources/script.js'. This request has been blocked; the content must be served over HTTPS.
 This test opens a window that loads a data: iframe that loads an insecure script, and that the script is still blocked. Although the data: frame has a separate origin, the script can still navigate top.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https-expected.txt
index 6107019..1179eb9 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE WARNING: line 16: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https.html' was loaded over HTTPS, but requested an insecure resource 'nonwebbyscheme://this-will-fail-but-no-mixed-content-error-should-appear'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https.html' was loaded over HTTPS, but requested an insecure resource 'nonwebbyscheme://this-will-fail-but-no-mixed-content-error-should-appear'. This content should also be served over HTTPS.
 This tests that non-webby URLs are not blocked as mixed content. The test passes if a mixed content warning is displayed and the load proceeds. The test fails if a mixed content error is displayed, and the load is blocked.
 
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame-expected.txt
index 7d6a912..f7a3beb0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE WARNING: line 8: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-redirect-http-to-https-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/resources/redir.php?url=https://127.0.0.1:8443/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-redirect-http-to-https-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/resources/redir.php?url=https://127.0.0.1:8443/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
 This test opens a window that loads an insecure iframe (via a tricky redirect). We should trigger a mixed content callback because the main frame in the window is HTTPS but is running content that can be controlled by an active network attacker.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame-expected.txt
index 7d6a912..f7a3beb0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame-expected.txt
@@ -1,2 +1,2 @@
-CONSOLE WARNING: line 8: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-redirect-http-to-https-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/resources/redir.php?url=https://127.0.0.1:8443/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
+CONSOLE WARNING: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-redirect-http-to-https-frame.html' was loaded over HTTPS, but requested an insecure resource 'http://127.0.0.1:8080/security/resources/redir.php?url=https://127.0.0.1:8443/security/mixedContent/resources/boring.html'. This content should also be served over HTTPS.
 This test opens a window that loads an insecure iframe (via a tricky redirect). We should trigger a mixed content callback because the main frame in the window is HTTPS but is running content that can be controlled by an active network attacker.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/strict-mode-image-in-frame-blocked.https-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/strict-mode-image-in-frame-blocked.https-expected.txt
index 2a1ca3e..ad3fc2b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/strict-mode-image-in-frame-blocked.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/mixedContent/strict-mode-image-in-frame-blocked.https-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/resources/frame-with-insecure-image.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This request has been blocked; the content must be served over HTTPS.
+CONSOLE ERROR: Mixed Content: The page at 'https://127.0.0.1:8443/security/mixedContent/strict-mode-image-in-frame-blocked.https.html' was loaded over HTTPS, but requested an insecure image 'http://127.0.0.1:8080/security/resources/compass.jpg'. This request has been blocked; the content must be served over HTTPS.
 This test passes if the image in the frame below is treated as blockable content.
 
 
diff --git a/third_party/WebKit/Source/core/animation/PathInterpolationFunctions.cpp b/third_party/WebKit/Source/core/animation/PathInterpolationFunctions.cpp
index 92a837b..f81ce2f 100644
--- a/third_party/WebKit/Source/core/animation/PathInterpolationFunctions.cpp
+++ b/third_party/WebKit/Source/core/animation/PathInterpolationFunctions.cpp
@@ -160,7 +160,7 @@
         toInterpolableList(*toInterpolableList(interpolableValue).get(PathArgsIndex)),
         toSVGPathNonInterpolableValue(nonInterpolableValue)->pathSegTypes());
     SVGPathByteStreamBuilder builder(*pathByteStream);
-    SVGPathParser(&source, &builder).parsePathDataFromSource(UnalteredParsing, false);
+    SVGPathParser(&source, &builder).parsePathDataFromSource(false);
     return pathByteStream.release();
 }
 
diff --git a/third_party/WebKit/Source/core/css/CSSValueTestHelper.h b/third_party/WebKit/Source/core/css/CSSValueTestHelper.h
index 59a3113..39b526a0 100644
--- a/third_party/WebKit/Source/core/css/CSSValueTestHelper.h
+++ b/third_party/WebKit/Source/core/css/CSSValueTestHelper.h
@@ -39,7 +39,7 @@
 #include "core/css/CSSPrimitiveValue.h"
 #include "core/css/CSSValue.h"
 
-#include <iostream>
+#include <ostream>
 
 namespace testing {
 namespace internal {
diff --git a/third_party/WebKit/Source/core/css/parser/CSSAtRuleID.cpp b/third_party/WebKit/Source/core/css/parser/CSSAtRuleID.cpp
index 9276fbc..809d035 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSAtRuleID.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSAtRuleID.cpp
@@ -11,25 +11,25 @@
 
 CSSAtRuleID cssAtRuleID(const CSSParserString& name)
 {
-    if (name.equalIgnoringCase("charset"))
+    if (name.equalIgnoringASCIICase("charset"))
         return CSSAtRuleCharset;
-    if (name.equalIgnoringCase("font-face"))
+    if (name.equalIgnoringASCIICase("font-face"))
         return CSSAtRuleFontFace;
-    if (name.equalIgnoringCase("import"))
+    if (name.equalIgnoringASCIICase("import"))
         return CSSAtRuleImport;
-    if (name.equalIgnoringCase("keyframes"))
+    if (name.equalIgnoringASCIICase("keyframes"))
         return CSSAtRuleKeyframes;
-    if (name.equalIgnoringCase("media"))
+    if (name.equalIgnoringASCIICase("media"))
         return CSSAtRuleMedia;
-    if (name.equalIgnoringCase("namespace"))
+    if (name.equalIgnoringASCIICase("namespace"))
         return CSSAtRuleNamespace;
-    if (name.equalIgnoringCase("page"))
+    if (name.equalIgnoringASCIICase("page"))
         return CSSAtRulePage;
-    if (name.equalIgnoringCase("supports"))
+    if (name.equalIgnoringASCIICase("supports"))
         return CSSAtRuleSupports;
-    if (name.equalIgnoringCase("viewport"))
+    if (name.equalIgnoringASCIICase("viewport"))
         return CSSAtRuleViewport;
-    if (name.equalIgnoringCase("-webkit-keyframes"))
+    if (name.equalIgnoringASCIICase("-webkit-keyframes"))
         return CSSAtRuleWebkitKeyframes;
     return CSSAtRuleInvalid;
 }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
index 93be952e..47300c6 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
@@ -358,7 +358,7 @@
     if (token.type() == StringToken || token.type() == UrlToken)
         return range.consumeIncludingWhitespace().value();
 
-    if (token.type() != FunctionToken || !token.valueEqualsIgnoringCase("url"))
+    if (token.type() != FunctionToken || !token.valueEqualsIgnoringASCIICase("url"))
         return AtomicString();
 
     CSSParserTokenRange contents = range.consumeBlock();
@@ -700,7 +700,7 @@
     const CSSParserToken* last = range.end() - 1;
     while (last->type() == WhitespaceToken)
         --last;
-    if (last->type() == IdentToken && last->valueEqualsIgnoringCase("important")) {
+    if (last->type() == IdentToken && last->valueEqualsIgnoringASCIICase("important")) {
         --last;
         while (last->type() == WhitespaceToken)
             --last;
@@ -748,9 +748,9 @@
         const CSSParserToken& token = range.consumeIncludingWhitespace();
         if (token.type() == PercentageToken && token.numericValue() >= 0 && token.numericValue() <= 100)
             result->append(token.numericValue() / 100);
-        else if (token.type() == IdentToken && token.valueEqualsIgnoringCase("from"))
+        else if (token.type() == IdentToken && token.valueEqualsIgnoringASCIICase("from"))
             result->append(0);
-        else if (token.type() == IdentToken && token.valueEqualsIgnoringCase("to"))
+        else if (token.type() == IdentToken && token.valueEqualsIgnoringASCIICase("to"))
             result->append(1);
         else
             return nullptr; // Parser error, invalid value in keyframe selector
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserString.h b/third_party/WebKit/Source/core/css/parser/CSSParserString.h
index 0a83860f..7b868fd 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserString.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserString.h
@@ -81,13 +81,12 @@
         return m_data.characters16[i];
     }
 
-    bool equalIgnoringCase(const char* str) const
+    template<unsigned matchLength>
+    bool equalIgnoringASCIICase(const char (&match)[matchLength]) const
     {
-        bool match = is8Bit() ? WTF::equalIgnoringCase(str, characters8(), length()) : WTF::equalIgnoringCase(str, characters16(), length());
-        if (!match)
+        if (matchLength - 1 != length())
             return false;
-        ASSERT(strlen(str) >= length());
-        return str[length()] == '\0';
+        return is8Bit() ? WTF::equalIgnoringASCIICase(characters8(), match, length()) : WTF::equalIgnoringASCIICase(characters16(), match, length());
     }
 
     operator String() const { return is8Bit() ? String(m_data.characters8, m_length) : StringImpl::create8BitIfPossible(m_data.characters16, m_length); }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserToken.h b/third_party/WebKit/Source/core/css/parser/CSSParserToken.h
index 88d00dc..811db85 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserToken.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserToken.h
@@ -128,7 +128,8 @@
         ret.initRaw(m_valueDataCharRaw, m_valueLength, m_valueIs8Bit);
         return ret;
     }
-    bool valueEqualsIgnoringCase(const char* str) const { return value().equalIgnoringCase(str); }
+    template<unsigned matchLength>
+    bool valueEqualsIgnoringASCIICase(const char (&match)[matchLength]) const { return value().equalIgnoringASCIICase<matchLength>(match); }
 
     UChar delimiter() const;
     NumericSign numericSign() const;
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserValues.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserValues.cpp
index 3aa4fe6..22d5adc 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserValues.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserValues.cpp
@@ -46,7 +46,7 @@
         CSSParserValue value;
         switch (token.type()) {
         case FunctionToken: {
-            if (token.valueEqualsIgnoringCase("url")) {
+            if (token.valueEqualsIgnoringASCIICase("url")) {
                 range.consume();
                 const CSSParserToken& next = range.consumeIncludingWhitespace();
                 if (next.type() == BadStringToken || range.consume().type() != RightParenthesisToken) {
@@ -59,7 +59,8 @@
                 value.m_unit = CSSParserValue::URI;
                 value.string = next.value();
                 break;
-            } else if (token.valueEqualsIgnoringCase("var")) {
+            }
+            if (token.valueEqualsIgnoringASCIICase("var")) {
                 destroyAndClear();
                 return;
             }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserValuesTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserValuesTest.cpp
index 02944863..e73995d 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserValuesTest.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserValuesTest.cpp
@@ -59,20 +59,20 @@
     EXPECT_FALSE(String(cssParserString).isNull());
 }
 
-TEST(CSSParserValuesTest, EqualIgnoringCase8BitsString)
+TEST(CSSParserValuesTest, EqualIgnoringASCIICase8BitsString)
 {
     CSSParserString cssParserString;
     String string8bit("sHaDOw");
     cssParserString.init(string8bit);
 
-    ASSERT_TRUE(cssParserString.equalIgnoringCase("shadow"));
-    ASSERT_TRUE(cssParserString.equalIgnoringCase("ShaDow"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("shadow-all"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("sha"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("abCD"));
+    ASSERT_TRUE(cssParserString.equalIgnoringASCIICase("shadow"));
+    ASSERT_TRUE(cssParserString.equalIgnoringASCIICase("ShaDow"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("shadow-all"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("sha"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("abCD"));
 }
 
-TEST(CSSParserValuesTest, EqualIgnoringCase16BitsString)
+TEST(CSSParserValuesTest, EqualIgnoringASCIICase16BitsString)
 {
     String string16bit("sHaDOw");
     string16bit.ensure16Bit();
@@ -80,11 +80,21 @@
     CSSParserString cssParserString;
     cssParserString.init(string16bit);
 
-    ASSERT_TRUE(cssParserString.equalIgnoringCase("shadow"));
-    ASSERT_TRUE(cssParserString.equalIgnoringCase("ShaDow"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("shadow-all"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("sha"));
-    ASSERT_FALSE(cssParserString.equalIgnoringCase("abCD"));
+    ASSERT_TRUE(cssParserString.equalIgnoringASCIICase("shadow"));
+    ASSERT_TRUE(cssParserString.equalIgnoringASCIICase("ShaDow"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("shadow-all"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("sha"));
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("abCD"));
+
+    StringBuilder builder;
+    builder.append(0x017f); // LATIN SMALL LETTER LONG S
+    builder.append("HaDOw");
+    ASSERT_FALSE(builder.is8Bit());
+
+    String string16BitFoldingToASCII = builder.toString();
+    cssParserString.init(string16BitFoldingToASCII);
+
+    ASSERT_FALSE(cssParserString.equalIgnoringASCIICase("shadow"));
 }
 
 TEST(CSSParserValuesTest, CSSParserValuelistClear)
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index f653367..88d4ec8 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -1534,7 +1534,7 @@
             context.useCounter()->count(UseCounter::QuotedAnimationName);
 
         const CSSParserToken& token = range.consumeIncludingWhitespace();
-        if (token.valueEqualsIgnoringCase("none"))
+        if (token.valueEqualsIgnoringASCIICase("none"))
             return cssValuePool().createIdentifierValue(CSSValueNone);
         return CSSCustomIdentValue::create(token.value());
     }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
index 4a90301..deeb93b 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
@@ -576,7 +576,7 @@
         return fallbackResult;
     range.consume();
     const CSSParserToken& ident = range.consume();
-    if (ident.type() != IdentToken || !ident.valueEqualsIgnoringCase("deep"))
+    if (ident.type() != IdentToken || !ident.valueEqualsIgnoringASCIICase("deep"))
         m_failedParsing = true;
     const CSSParserToken& slash = range.consumeIncludingWhitespace();
     if (slash.type() != DelimiterToken || slash.delimiter() != '/')
@@ -612,7 +612,7 @@
     if (range.peek().type() != IdentToken)
         return CSSSelector::CaseSensitive;
     const CSSParserToken& flag = range.consumeIncludingWhitespace();
-    if (flag.valueEqualsIgnoringCase("i"))
+    if (flag.valueEqualsIgnoringASCIICase("i"))
         return CSSSelector::CaseInsensitive;
     m_failedParsing = true;
     return CSSSelector::CaseSensitive;
@@ -626,11 +626,11 @@
         return true;
     }
     if (token.type() == IdentToken) {
-        if (token.valueEqualsIgnoringCase("odd")) {
+        if (token.valueEqualsIgnoringASCIICase("odd")) {
             result = std::make_pair(2, 1);
             return true;
         }
-        if (token.valueEqualsIgnoringCase("even")) {
+        if (token.valueEqualsIgnoringASCIICase("even")) {
             result = std::make_pair(2, 0);
             return true;
         }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSupportsParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSSupportsParser.cpp
index 7764bff..56ecaf2 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSSupportsParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSSupportsParser.cpp
@@ -50,8 +50,8 @@
             return Invalid;
         if (clauseType == Unresolved)
             clauseType = token.value().length() == 3 ? Conjunction : Disjunction;
-        if ((clauseType == Conjunction && !equalIgnoringCase(token.value(), "and"))
-            || (clauseType == Disjunction && !equalIgnoringCase(token.value(), "or")))
+        if ((clauseType == Conjunction && !token.valueEqualsIgnoringASCIICase("and"))
+            || (clauseType == Disjunction && !token.valueEqualsIgnoringASCIICase("or")))
             return Invalid;
 
         if (range.consumeIncludingWhitespace().type() != WhitespaceToken)
@@ -63,7 +63,7 @@
 CSSSupportsParser::SupportsResult CSSSupportsParser::consumeNegation(CSSParserTokenRange range)
 {
     ASSERT(range.peek().type() == IdentToken);
-    if (!equalIgnoringCase(range.consume().value(), "not"))
+    if (!range.consume().valueEqualsIgnoringASCIICase("not"))
         return Invalid;
     if (range.consumeIncludingWhitespace().type() != WhitespaceToken)
         return Invalid;
diff --git a/third_party/WebKit/Source/core/css/parser/CSSTokenizer.cpp b/third_party/WebKit/Source/core/css/parser/CSSTokenizer.cpp
index 31109d9e..6d4a2abd 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSTokenizer.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSTokenizer.cpp
@@ -483,7 +483,7 @@
 {
     CSSParserString name = consumeName();
     if (consumeIfNext('(')) {
-        if (name.equalIgnoringCase("url")) {
+        if (name.equalIgnoringASCIICase("url")) {
             // The spec is slightly different so as to avoid dropping whitespace
             // tokens, but they wouldn't be used and this is easier.
             consumeUntilNonWhitespace();
diff --git a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
index 1d92dc9..d8847a98 100644
--- a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
@@ -55,16 +55,6 @@
 
 namespace blink {
 
-template <unsigned N>
-static bool equalIgnoringCase(const CSSParserString& a, const char (&b)[N])
-{
-    unsigned length = N - 1; // Ignore the trailing null character
-    if (a.length() != length)
-        return false;
-
-    return a.is8Bit() ? WTF::equalIgnoringCase(b, a.characters8(), length) : WTF::equalIgnoringCase(b, a.characters16(), length);
-}
-
 void CSSPropertyParser::addProperty(CSSPropertyID propId, PassRefPtrWillBeRawPtr<CSSValue> value, bool important, bool implicit)
 {
     ASSERT(!isPropertyAlias(propId));
diff --git a/third_party/WebKit/Source/core/css/parser/MediaQueryParser.cpp b/third_party/WebKit/Source/core/css/parser/MediaQueryParser.cpp
index 8df06e3..cfb23db 100644
--- a/third_party/WebKit/Source/core/css/parser/MediaQueryParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/MediaQueryParser.cpp
@@ -65,19 +65,19 @@
 
 void MediaQueryParser::readMediaNot(CSSParserTokenType type, const CSSParserToken& token)
 {
-    if (type == IdentToken && equalIgnoringCase(token.value(), "not"))
+    if (type == IdentToken && token.valueEqualsIgnoringASCIICase("not"))
         setStateAndRestrict(ReadFeatureStart, MediaQuery::Not);
     else
         readFeatureStart(type, token);
 }
 
-static bool isRestrictorOrLogicalOperator(const String& tokenValue)
+static bool isRestrictorOrLogicalOperator(const CSSParserToken& token)
 {
     // FIXME: it would be more efficient to use lower-case always for tokenValue.
-    return equalIgnoringCase(tokenValue, "not")
-        || equalIgnoringCase(tokenValue, "and")
-        || equalIgnoringCase(tokenValue, "or")
-        || equalIgnoringCase(tokenValue, "only");
+    return token.valueEqualsIgnoringASCIICase("not")
+        || token.valueEqualsIgnoringASCIICase("and")
+        || token.valueEqualsIgnoringASCIICase("or")
+        || token.valueEqualsIgnoringASCIICase("only");
 }
 
 void MediaQueryParser::readMediaType(CSSParserTokenType type, const CSSParserToken& token)
@@ -88,12 +88,12 @@
         else
             m_state = ReadFeature;
     } else if (type == IdentToken) {
-        if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "not")) {
+        if (m_state == ReadRestrictor && token.valueEqualsIgnoringASCIICase("not")) {
             setStateAndRestrict(ReadMediaType, MediaQuery::Not);
-        } else if (m_state == ReadRestrictor && equalIgnoringCase(token.value(), "only")) {
+        } else if (m_state == ReadRestrictor && token.valueEqualsIgnoringASCIICase("only")) {
             setStateAndRestrict(ReadMediaType, MediaQuery::Only);
         } else if (m_mediaQueryData.restrictor() != MediaQuery::None
-            && isRestrictorOrLogicalOperator(token.value())) {
+            && isRestrictorOrLogicalOperator(token)) {
             m_state = SkipUntilComma;
         } else {
             m_mediaQueryData.setMediaType(token.value());
@@ -110,7 +110,7 @@
 
 void MediaQueryParser::readAnd(CSSParserTokenType type, const CSSParserToken& token)
 {
-    if (type == IdentToken && equalIgnoringCase(token.value(), "and")) {
+    if (type == IdentToken && token.valueEqualsIgnoringASCIICase("and")) {
         m_state = ReadFeatureStart;
     } else if (type == CommaToken && m_parserType != MediaConditionParser) {
         m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
diff --git a/third_party/WebKit/Source/core/css/parser/SizesCalcParser.cpp b/third_party/WebKit/Source/core/css/parser/SizesCalcParser.cpp
index 4dd16c9a..889607d 100644
--- a/third_party/WebKit/Source/core/css/parser/SizesCalcParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/SizesCalcParser.cpp
@@ -105,7 +105,7 @@
                 return false;
             break;
         case FunctionToken:
-            if (!token.valueEqualsIgnoringCase("calc"))
+            if (!token.valueEqualsIgnoringASCIICase("calc"))
                 return false;
             // "calc(" is the same as "("
         case LeftParenthesisToken:
diff --git a/third_party/WebKit/Source/core/fetch/Resource.cpp b/third_party/WebKit/Source/core/fetch/Resource.cpp
index 33a2833..ecb5e43 100644
--- a/third_party/WebKit/Source/core/fetch/Resource.cpp
+++ b/third_party/WebKit/Source/core/fetch/Resource.cpp
@@ -1115,6 +1115,8 @@
         return "XSL stylesheet";
     case Resource::LinkPrefetch:
         return "Link prefetch resource";
+    case Resource::LinkPreload:
+        return "Link preload resource";
     case Resource::LinkSubresource:
         return "Link subresource";
     case Resource::TextTrack:
@@ -1149,6 +1151,7 @@
         return true;
     case Resource::Raw:
     case Resource::LinkPrefetch:
+    case Resource::LinkPreload:
     case Resource::LinkSubresource:
     case Resource::TextTrack:
     case Resource::Media:
@@ -1182,6 +1185,8 @@
         return "XSLStyleSheet";
     case Resource::LinkPrefetch:
         return "LinkPrefetch";
+    case Resource::LinkPreload:
+        return "LinkPreload";
     case Resource::LinkSubresource:
         return "LinkSubresource";
     case Resource::TextTrack:
diff --git a/third_party/WebKit/Source/core/fetch/Resource.h b/third_party/WebKit/Source/core/fetch/Resource.h
index 094129053..33c12f1 100644
--- a/third_party/WebKit/Source/core/fetch/Resource.h
+++ b/third_party/WebKit/Source/core/fetch/Resource.h
@@ -77,6 +77,7 @@
         XSLStyleSheet,
         LinkPrefetch,
         LinkSubresource,
+        LinkPreload,
         TextTrack,
         ImportResource,
         Media, // Audio or video file requested by a HTML5 media element
@@ -125,7 +126,6 @@
 
     virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; }
 
-    ResourceRequest& mutableResourceRequest() { return m_resourceRequest; }
     const ResourceRequest& resourceRequest() const { return m_resourceRequest; }
     const ResourceRequest& lastResourceRequest() const;
 
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
index 36307ed3..08cf67e 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
@@ -89,6 +89,7 @@
     case Resource::Manifest:
         return ResourceLoadPriorityMedium;
     case Resource::LinkSubresource:
+    case Resource::LinkPreload:
     case Resource::TextTrack:
     case Resource::Media:
     case Resource::SVGDocument:
@@ -151,6 +152,8 @@
         return WebURLRequest::RequestContextImport;
     case Resource::LinkPrefetch:
         return WebURLRequest::RequestContextPrefetch;
+    case Resource::LinkPreload:
+        return WebURLRequest::RequestContextSubresource;
     case Resource::LinkSubresource:
         return WebURLRequest::RequestContextSubresource;
     case Resource::TextTrack:
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index df0dd0e..8442461f 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -203,8 +203,6 @@
     String sourceOrigin = sourceDocument->securityOrigin()->toString();
     String sourceSuborigin = sourceDocument->securityOrigin()->suboriginName();
 
-    // FIXME: MixedContentChecker needs to be refactored for OOPIF.  For now,
-    // create the url using replicated origins for remote frames.
     KURL targetUrl = isLocalDOMWindow() ? document()->url() : KURL(KURL(), frame()->securityContext()->securityOrigin()->toString());
     if (MixedContentChecker::isMixedContent(sourceDocument->securityOrigin(), targetUrl))
         UseCounter::count(frame(), UseCounter::PostMessageFromSecureToInsecure);
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index f94c98e..a6304a7 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -3727,16 +3727,11 @@
 
 void FrameView::paint(GraphicsContext& context, const GlobalPaintFlags globalPaintFlags, const CullRect& cullRect) const
 {
-    // TODO(skyostil): Remove this early-out in favor of painting cached scrollbars.
-    if (shouldThrottleRendering())
-        return;
     FramePainter(*this).paint(context, globalPaintFlags, cullRect);
 }
 
 void FrameView::paintContents(GraphicsContext& context, const GlobalPaintFlags globalPaintFlags, const IntRect& damageRect) const
 {
-    if (shouldThrottleRendering())
-        return;
     FramePainter(*this).paintContents(context, globalPaintFlags, damageRect);
 }
 
@@ -4032,8 +4027,15 @@
     }
 
     bool becameUnthrottled = wasThrottled && !canThrottleRendering();
-    if (becameUnthrottled)
+    if (becameUnthrottled) {
+        // Start ticking animation frames again if necessary.
         page()->animator().scheduleVisualUpdate(m_frame.get());
+        // Force a full repaint of this frame to ensure we are not left with a
+        // partially painted version of this frame's contents if we skipped
+        // painting them while the frame was throttled.
+        if (LayoutView* layoutView = this->layoutView())
+            layoutView->setShouldDoFullPaintInvalidation(PaintInvalidationBecameVisible);
+    }
 }
 
 bool FrameView::shouldThrottleRendering() const
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
index d119619..bb4e23a9 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
@@ -365,7 +365,7 @@
 TEST_F(HTMLPreloadScannerTest, testLinkRelPreload)
 {
     TestCase testCases[] = {
-        {"http://example.test", "<link rel=preload href=bla>", "bla", "http://example.test/", Resource::LinkSubresource, 0},
+        {"http://example.test", "<link rel=preload href=bla>", "bla", "http://example.test/", Resource::LinkPreload, 0},
         {"http://example.test", "<link rel=preload href=bla as=script>", "bla", "http://example.test/", Resource::Script, 0},
         {"http://example.test", "<link rel=preload href=bla as=style>", "bla", "http://example.test/", Resource::CSSStyleSheet, 0},
         {"http://example.test", "<link rel=preload href=bla as=image>", "bla", "http://example.test/", Resource::Image, 0},
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index b0cb299..65fcc59 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -2679,7 +2679,7 @@
 
 LayoutUnit LayoutBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
 {
-    LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().hasPercent()) || style()->logicalMinWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(MinSize, style()->logicalMinWidth());
+    LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().hasPercent()) ? logicalWidth : computeReplacedLogicalWidthUsing(MinSize, style()->logicalMinWidth());
     LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().hasPercent()) || style()->logicalMaxWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(MaxSize, style()->logicalMaxWidth());
     return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth));
 }
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index e33f8df5..d337666 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -187,7 +187,7 @@
     case Resource::ImportResource:
         resource = RawResource::fetchImport(request, fetcher());
         break;
-    case Resource::LinkSubresource:
+    case Resource::LinkPreload:
         resource = RawResource::fetch(request, fetcher());
         break;
     default:
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 27d73c6..000e8f9 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -428,6 +428,7 @@
     case Resource::Font:
     case Resource::Raw:
     case Resource::LinkPrefetch:
+    case Resource::LinkPreload:
     case Resource::LinkSubresource:
     case Resource::TextTrack:
     case Resource::ImportResource:
@@ -505,6 +506,11 @@
             return ResourceRequestBlockedReasonCSP;
         break;
     }
+    case Resource::LinkPreload:
+        ASSERT(csp);
+        if (!shouldBypassMainWorldCSP && !csp->allowConnectToSource(url, redirectStatus, cspReporting))
+            return ResourceRequestBlockedReasonCSP;
+        break;
     case Resource::MainResource:
     case Resource::Raw:
     case Resource::LinkPrefetch:
@@ -559,7 +565,7 @@
     // They'll still get a warning in the console about CSP blocking the load.
     MixedContentChecker::ReportingStatus mixedContentReporting = forPreload ?
         MixedContentChecker::SuppressReport : MixedContentChecker::SendReport;
-    if (MixedContentChecker::shouldBlockFetch(MixedContentChecker::effectiveFrameForFrameType(frame(), resourceRequest.frameType()), resourceRequest, url, mixedContentReporting))
+    if (MixedContentChecker::shouldBlockFetch(frame(), resourceRequest, url, mixedContentReporting))
         return ResourceRequestBlockedReasonMixedContent;
 
     return ResourceRequestBlockedReasonNone;
diff --git a/third_party/WebKit/Source/core/loader/LinkLoader.cpp b/third_party/WebKit/Source/core/loader/LinkLoader.cpp
index 05fd8bc..e0f24c16 100644
--- a/third_party/WebKit/Source/core/loader/LinkLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/LinkLoader.cpp
@@ -184,8 +184,7 @@
         return Resource::TextTrack;
     if (document && !as.isEmpty())
         document->addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> must have a valid `as` value")));
-    // TODO(yoav): Is this correct? If as is missing or invalid, it should be subject to "connect-src" CSP directives.
-    return Resource::LinkSubresource;
+    return Resource::LinkPreload;
 }
 
 void LinkLoader::createLinkPreloadResourceClient(ResourcePtr<Resource> resource)
diff --git a/third_party/WebKit/Source/core/loader/MixedContentChecker.cpp b/third_party/WebKit/Source/core/loader/MixedContentChecker.cpp
index 0b0dd21..a1bad4b 100644
--- a/third_party/WebKit/Source/core/loader/MixedContentChecker.cpp
+++ b/third_party/WebKit/Source/core/loader/MixedContentChecker.cpp
@@ -29,6 +29,7 @@
 #include "core/loader/MixedContentChecker.h"
 
 #include "core/dom/Document.h"
+#include "core/frame/Frame.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/Settings.h"
 #include "core/frame/UseCounter.h"
@@ -44,22 +45,37 @@
 
 namespace blink {
 
-static void measureStricterVersionOfIsMixedContent(LocalFrame* frame, const KURL& url)
+namespace {
+
+// When a frame is local, use its full URL to represent the main
+// resource. When the frame is remote, the full URL isn't accessible, so
+// use the origin. This function is used, for example, to determine the
+// URL to show in console messages about mixed content.
+KURL mainResourceUrlForFrame(Frame* frame)
+{
+    if (frame->isRemoteFrame())
+        return KURL(KURL(), frame->securityContext()->securityOrigin()->toString());
+    return toLocalFrame(frame)->document()->url();
+}
+
+} // namespace
+
+static void measureStricterVersionOfIsMixedContent(Frame* frame, const KURL& url)
 {
     // We're currently only checking for mixed content in `https://*` contexts.
     // What about other "secure" contexts the SchemeRegistry knows about? We'll
     // use this method to measure the occurance of non-webby mixed content to
     // make sure we're not breaking the world without realizing it.
-    SecurityOrigin* origin = frame->document()->securityOrigin();
+    SecurityOrigin* origin = frame->securityContext()->securityOrigin();
     if (MixedContentChecker::isMixedContent(origin, url)) {
-        if (frame->document()->securityOrigin()->protocol() != "https")
+        if (origin->protocol() != "https")
             UseCounter::count(frame, UseCounter::MixedContentInNonHTTPSFrameThatRestrictsMixedContent);
     } else if (!SecurityOrigin::isSecure(url) && SchemeRegistry::shouldTreatURLSchemeAsSecure(origin->protocol())) {
         UseCounter::count(frame, UseCounter::MixedContentInSecureFrameThatDoesNotRestrictMixedContent);
     }
 }
 
-bool requestIsSubframeSubresource(LocalFrame* frame, WebURLRequest::FrameType frameType)
+bool requestIsSubframeSubresource(Frame* frame, WebURLRequest::FrameType frameType)
 {
     return (frame && frame != frame->tree().top() && frameType != WebURLRequest::FrameTypeNested);
 }
@@ -75,7 +91,7 @@
 }
 
 // static
-LocalFrame* MixedContentChecker::inWhichFrameIsContentMixed(LocalFrame* frame, WebURLRequest::FrameType frameType, const KURL& url)
+Frame* MixedContentChecker::inWhichFrameIsContentMixed(Frame* frame, WebURLRequest::FrameType frameType, const KURL& url)
 {
     // We only care about subresource loads; top-level navigations cannot be mixed content. Neither can frameless requests.
     if (frameType == WebURLRequest::FrameTypeTopLevel || !frame)
@@ -83,19 +99,13 @@
 
     // Check the top frame first.
     if (Frame* top = frame->tree().top()) {
-        // FIXME: We need a way to access the top-level frame's SecurityOrigin when that frame
-        // is in a different process from the current frame. Until that is done, we bail out.
-        if (!top->isLocalFrame())
-            return nullptr;
-
-        LocalFrame* localTop = toLocalFrame(top);
-        measureStricterVersionOfIsMixedContent(localTop, url);
-        if (isMixedContent(localTop->document()->securityOrigin(), url))
-            return localTop;
+        measureStricterVersionOfIsMixedContent(top, url);
+        if (isMixedContent(top->securityContext()->securityOrigin(), url))
+            return top;
     }
 
     measureStricterVersionOfIsMixedContent(frame, url);
-    if (isMixedContent(frame->document()->securityOrigin(), url))
+    if (isMixedContent(frame->securityContext()->securityOrigin(), url))
         return frame;
 
     // No mixed content, no problem.
@@ -103,7 +113,7 @@
 }
 
 // static
-MixedContentChecker::ContextType MixedContentChecker::contextTypeFromContext(WebURLRequest::RequestContext context, LocalFrame* frame)
+MixedContentChecker::ContextType MixedContentChecker::contextTypeFromContext(WebURLRequest::RequestContext context, Frame* frame)
 {
     switch (context) {
     // "Optionally-blockable" mixed content
@@ -238,18 +248,18 @@
 }
 
 // static
-void MixedContentChecker::logToConsoleAboutFetch(LocalFrame* frame, const KURL& url, WebURLRequest::RequestContext requestContext, bool allowed)
+void MixedContentChecker::logToConsoleAboutFetch(LocalFrame* frame, const KURL& mainResourceUrl, const KURL& url, WebURLRequest::RequestContext requestContext, bool allowed)
 {
     String message = String::format(
         "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an insecure %s '%s'. %s",
-        frame->document()->url().elidedString().utf8().data(), typeNameFromContext(requestContext), url.elidedString().utf8().data(),
+        mainResourceUrl.elidedString().utf8().data(), typeNameFromContext(requestContext), url.elidedString().utf8().data(),
         allowed ? "This content should also be served over HTTPS." : "This request has been blocked; the content must be served over HTTPS.");
     MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
     frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message));
 }
 
 // static
-void MixedContentChecker::count(LocalFrame* frame, WebURLRequest::RequestContext requestContext)
+void MixedContentChecker::count(Frame* frame, WebURLRequest::RequestContext requestContext)
 {
     UseCounter::count(frame, UseCounter::MixedContentPresent);
 
@@ -298,15 +308,19 @@
 // static
 bool MixedContentChecker::shouldBlockFetch(LocalFrame* frame, WebURLRequest::RequestContext requestContext, WebURLRequest::FrameType frameType, const KURL& url, MixedContentChecker::ReportingStatus reportingStatus)
 {
-    LocalFrame* mixedFrame = inWhichFrameIsContentMixed(frame, frameType, url);
+    Frame* effectiveFrame = effectiveFrameForFrameType(frame, frameType);
+    Frame* mixedFrame = inWhichFrameIsContentMixed(effectiveFrame, frameType, url);
     if (!mixedFrame)
         return false;
 
     MixedContentChecker::count(mixedFrame, requestContext);
 
     Settings* settings = mixedFrame->settings();
-    FrameLoaderClient* client = mixedFrame->loader().client();
-    SecurityOrigin* securityOrigin = mixedFrame->document()->securityOrigin();
+    // Use the current local frame's client; the embedder doesn't
+    // distinguish mixed content signals from different frames on the
+    // same page.
+    FrameLoaderClient* client = frame->loader().client();
+    SecurityOrigin* securityOrigin = mixedFrame->securityContext()->securityOrigin();
     bool allowed = false;
 
     // If we're in strict mode, we'll automagically fail everything, and intentionally skip
@@ -334,7 +348,7 @@
     case ContextTypeBlockable: {
         // Strictly block subresources in subframes, unless all insecure
         // content is allowed.
-        if (!settings->allowRunningOfInsecureContent() && requestIsSubframeSubresource(frame, frameType)) {
+        if (!settings->allowRunningOfInsecureContent() && requestIsSubframeSubresource(effectiveFrame, frameType)) {
             UseCounter::count(mixedFrame, UseCounter::BlockableMixedContentInSubframeBlocked);
             allowed = false;
             break;
@@ -360,16 +374,16 @@
     };
 
     if (reportingStatus == SendReport)
-        logToConsoleAboutFetch(frame, url, requestContext, allowed);
+        logToConsoleAboutFetch(frame, mainResourceUrlForFrame(mixedFrame), url, requestContext, allowed);
     return !allowed;
 }
 
 // static
-void MixedContentChecker::logToConsoleAboutWebSocket(LocalFrame* frame, const KURL& url, bool allowed)
+void MixedContentChecker::logToConsoleAboutWebSocket(LocalFrame* frame, const KURL& mainResourceUrl, const KURL& url, bool allowed)
 {
     String message = String::format(
         "Mixed Content: The page at '%s' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint '%s'. %s",
-        frame->document()->url().elidedString().utf8().data(), url.elidedString().utf8().data(),
+        mainResourceUrl.elidedString().utf8().data(), url.elidedString().utf8().data(),
         allowed ? "This endpoint should be available via WSS. Insecure access is deprecated." : "This request has been blocked; this endpoint must be available over WSS.");
     MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
     frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message));
@@ -378,7 +392,7 @@
 // static
 bool MixedContentChecker::shouldBlockWebSocket(LocalFrame* frame, const KURL& url, MixedContentChecker::ReportingStatus reportingStatus)
 {
-    LocalFrame* mixedFrame = inWhichFrameIsContentMixed(frame, WebURLRequest::FrameTypeNone, url);
+    Frame* mixedFrame = inWhichFrameIsContentMixed(frame, WebURLRequest::FrameTypeNone, url);
     if (!mixedFrame)
         return false;
 
@@ -386,13 +400,16 @@
     UseCounter::count(mixedFrame, UseCounter::MixedContentWebSocket);
 
     Settings* settings = mixedFrame->settings();
-    FrameLoaderClient* client = mixedFrame->loader().client();
-    SecurityOrigin* securityOrigin = mixedFrame->document()->securityOrigin();
+    // Use the current local frame's client; the embedder doesn't
+    // distinguish mixed content signals from different frames on the
+    // same page.
+    FrameLoaderClient* client = frame->loader().client();
+    SecurityOrigin* securityOrigin = mixedFrame->securityContext()->securityOrigin();
     bool allowed = false;
 
     // If we're in strict mode, we'll automagically fail everything, and intentionally skip
     // the client checks in order to prevent degrading the site's security UI.
-    bool strictMode = mixedFrame->document()->shouldEnforceStrictMixedContentChecking() || settings->strictMixedContentChecking();
+    bool strictMode = mixedFrame->securityContext()->shouldEnforceStrictMixedContentChecking() || settings->strictMixedContentChecking();
     if (!strictMode) {
         bool allowedPerSettings = settings && settings->allowRunningOfInsecureContent();
         allowed = client->allowRunningInsecureContent(allowedPerSettings, securityOrigin, url);
@@ -402,7 +419,7 @@
         client->didRunInsecureContent(securityOrigin, url);
 
     if (reportingStatus == SendReport)
-        logToConsoleAboutWebSocket(frame, url, allowed);
+        logToConsoleAboutWebSocket(frame, mainResourceUrlForFrame(mixedFrame), url, allowed);
     return !allowed;
 }
 
@@ -414,19 +431,22 @@
     if (url.protocolIs("javascript"))
         return false;
 
-    LocalFrame* mixedFrame = inWhichFrameIsContentMixed(frame, WebURLRequest::FrameTypeNone, url);
+    Frame* mixedFrame = inWhichFrameIsContentMixed(frame, WebURLRequest::FrameTypeNone, url);
     if (!mixedFrame)
         return false;
 
     UseCounter::count(mixedFrame, UseCounter::MixedContentPresent);
 
-    mixedFrame->loader().client()->didDisplayInsecureContent();
+    // Use the current local frame's client; the embedder doesn't
+    // distinguish mixed content signals from different frames on the
+    // same page.
+    frame->loader().client()->didDisplayInsecureContent();
 
     if (reportingStatus == SendReport) {
         String message = String::format(
             "Mixed Content: The page at '%s' was loaded over a secure connection, but contains a form which targets an insecure endpoint '%s'. This endpoint should be made available over a secure connection.",
-            frame->document()->url().elidedString().utf8().data(), url.elidedString().utf8().data());
-        mixedFrame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, WarningMessageLevel, message));
+            mainResourceUrlForFrame(mixedFrame).elidedString().utf8().data(), url.elidedString().utf8().data());
+        frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, WarningMessageLevel, message));
     }
 
     return true;
@@ -442,47 +462,53 @@
         UseCounter::count(frame->document(), UseCounter::MixedContentPrivateHostnameInPublicHostname);
 }
 
-LocalFrame* MixedContentChecker::effectiveFrameForFrameType(LocalFrame* frame, WebURLRequest::FrameType frameType)
+Frame* MixedContentChecker::effectiveFrameForFrameType(LocalFrame* frame, WebURLRequest::FrameType frameType)
 {
     // If we're loading the main resource of a subframe, ensure that we check
     // against the parent of the active frame, rather than the frame itself.
-    LocalFrame* effectiveFrame = frame;
-    if (frameType == WebURLRequest::FrameTypeNested) {
-        // FIXME: Deal with RemoteFrames.
-        Frame* parentFrame = effectiveFrame->tree().parent();
-        ASSERT(parentFrame);
-        if (parentFrame->isLocalFrame())
-            effectiveFrame = toLocalFrame(parentFrame);
-    }
-    return effectiveFrame;
+    if (frameType != WebURLRequest::FrameTypeNested)
+        return frame;
+
+    Frame* parentFrame = frame->tree().parent();
+    ASSERT(parentFrame);
+    return parentFrame;
 }
 
 void MixedContentChecker::handleCertificateError(LocalFrame* frame, const ResourceRequest& request, const ResourceResponse& response)
 {
     WebURLRequest::FrameType frameType = request.frameType();
-    LocalFrame* effectiveFrame = effectiveFrameForFrameType(frame, frameType);
+    Frame* effectiveFrame = effectiveFrameForFrameType(frame, frameType);
     if (frameType == WebURLRequest::FrameTypeTopLevel || !effectiveFrame)
         return;
 
-    FrameLoaderClient* client = effectiveFrame->loader().client();
+    // TODO(estark): handle remote frames, perhaps by omitting security info when the effective frame is remote.
+    if (!effectiveFrame->isLocalFrame())
+        return;
+
+    LocalFrame* localEffectiveFrame = toLocalFrame(effectiveFrame);
+
+    // Use the current local frame's client; the embedder doesn't
+    // distinguish mixed content signals from different frames on the
+    // same page.
+    FrameLoaderClient* client = frame->loader().client();
     WebURLRequest::RequestContext requestContext = request.requestContext();
-    ContextType contextType = MixedContentChecker::contextTypeFromContext(requestContext, frame);
+    ContextType contextType = MixedContentChecker::contextTypeFromContext(requestContext, effectiveFrame);
     if (contextType == ContextTypeBlockable) {
-        client->didRunContentWithCertificateErrors(response.url(), response.getSecurityInfo(), effectiveFrame->document()->url(), effectiveFrame->loader().documentLoader()->response().getSecurityInfo());
+        client->didRunContentWithCertificateErrors(response.url(), response.getSecurityInfo(), mainResourceUrlForFrame(effectiveFrame), localEffectiveFrame->loader().documentLoader()->response().getSecurityInfo());
     } else {
         // contextTypeFromContext() never returns NotMixedContent (it
         // computes the type of mixed content, given that the content is
         // mixed).
         ASSERT(contextType != ContextTypeNotMixedContent);
-        client->didDisplayContentWithCertificateErrors(response.url(), response.getSecurityInfo(), effectiveFrame->document()->url(), effectiveFrame->loader().documentLoader()->response().getSecurityInfo());
+        client->didDisplayContentWithCertificateErrors(response.url(), response.getSecurityInfo(), mainResourceUrlForFrame(effectiveFrame), localEffectiveFrame->loader().documentLoader()->response().getSecurityInfo());
     }
 }
 
 MixedContentChecker::ContextType MixedContentChecker::contextTypeForInspector(LocalFrame* frame, const ResourceRequest& request)
 {
-    LocalFrame* effectiveFrame = effectiveFrameForFrameType(frame, request.frameType());
+    Frame* effectiveFrame = effectiveFrameForFrameType(frame, request.frameType());
 
-    LocalFrame* mixedFrame = inWhichFrameIsContentMixed(effectiveFrame, request.frameType(), request.url());
+    Frame* mixedFrame = inWhichFrameIsContentMixed(effectiveFrame, request.frameType(), request.url());
     if (!mixedFrame)
         return ContextTypeNotMixedContent;
 
diff --git a/third_party/WebKit/Source/core/loader/MixedContentChecker.h b/third_party/WebKit/Source/core/loader/MixedContentChecker.h
index e2e50c4..a12196f 100644
--- a/third_party/WebKit/Source/core/loader/MixedContentChecker.h
+++ b/third_party/WebKit/Source/core/loader/MixedContentChecker.h
@@ -40,6 +40,7 @@
 
 namespace blink {
 
+class Frame;
 class FrameLoaderClient;
 class LocalFrame;
 class KURL;
@@ -75,7 +76,7 @@
 
     // Returns the frame that should be considered the effective frame
     // for a mixed content check for the given frame type.
-    static LocalFrame* effectiveFrameForFrameType(LocalFrame*, WebURLRequest::FrameType);
+    static Frame* effectiveFrameForFrameType(LocalFrame*, WebURLRequest::FrameType);
 
     static void handleCertificateError(LocalFrame*, const ResourceRequest&, const ResourceResponse&);
 
@@ -88,13 +89,13 @@
         Submission
     };
 
-    static LocalFrame* inWhichFrameIsContentMixed(LocalFrame*, WebURLRequest::FrameType, const KURL&);
+    static Frame* inWhichFrameIsContentMixed(Frame*, WebURLRequest::FrameType, const KURL&);
 
-    static ContextType contextTypeFromContext(WebURLRequest::RequestContext, LocalFrame*);
+    static ContextType contextTypeFromContext(WebURLRequest::RequestContext, Frame*);
     static const char* typeNameFromContext(WebURLRequest::RequestContext);
-    static void logToConsoleAboutFetch(LocalFrame*, const KURL&, WebURLRequest::RequestContext, bool allowed);
-    static void logToConsoleAboutWebSocket(LocalFrame*, const KURL&, bool allowed);
-    static void count(LocalFrame*, WebURLRequest::RequestContext);
+    static void logToConsoleAboutFetch(LocalFrame*, const KURL&, const KURL&, WebURLRequest::RequestContext, bool allowed);
+    static void logToConsoleAboutWebSocket(LocalFrame*, const KURL&, const KURL&, bool allowed);
+    static void count(Frame*, WebURLRequest::RequestContext);
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/FramePainter.cpp b/third_party/WebKit/Source/core/paint/FramePainter.cpp
index a6eb08c9..ba89543c 100644
--- a/third_party/WebKit/Source/core/paint/FramePainter.cpp
+++ b/third_party/WebKit/Source/core/paint/FramePainter.cpp
@@ -111,8 +111,10 @@
         return;
     }
 
-    RELEASE_ASSERT(!frameView().needsLayout());
-    ASSERT(document->lifecycle().state() >= DocumentLifecycle::CompositingClean);
+    if (!frameView().shouldThrottleRendering()) {
+        RELEASE_ASSERT(!frameView().needsLayout());
+        ASSERT(document->lifecycle().state() >= DocumentLifecycle::CompositingClean);
+    }
 
     TRACE_EVENT1("devtools.timeline", "Paint", "data", InspectorPaintEvent::data(layoutView, LayoutRect(rect), 0));
 
@@ -133,7 +135,8 @@
     PaintLayer* rootLayer = layoutView->layer();
 
 #if ENABLE(ASSERT)
-    layoutView->assertSubtreeIsLaidOut();
+    if (!frameView().shouldThrottleRendering())
+        layoutView->assertSubtreeIsLaidOut();
     LayoutObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(*rootLayer->layoutObject());
 #endif
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 31bbc5ce..4eea6f7 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -4,6 +4,7 @@
 
 #include "core/paint/PaintLayerPainter.h"
 
+#include "core/frame/FrameView.h"
 #include "core/frame/Settings.h"
 #include "core/layout/ClipPathOperation.h"
 #include "core/layout/LayoutBlock.h"
@@ -80,8 +81,7 @@
     if (shouldSuppressPaintingLayer(&m_paintLayer))
         return FullyPainted;
 
-    // TODO(skyostil): Unify this early-out logic with subsequence caching.
-    if (m_paintLayer.layoutObject()->isLayoutPart() && toLayoutPart(m_paintLayer.layoutObject())->isThrottledFrameView())
+    if (m_paintLayer.layoutObject()->isLayoutView() && toLayoutView(m_paintLayer.layoutObject())->frameView()->shouldThrottleRendering())
         return FullyPainted;
 
     // If this layer is totally invisible then there is nothing to paint.
@@ -270,9 +270,8 @@
     if (paintFlags & PaintLayerPaintingRootBackgroundOnly && !m_paintLayer.layoutObject()->isLayoutView() && !m_paintLayer.layoutObject()->isDocumentElement())
         return result;
 
-    // TODO(skyostil): Unify this early-out logic with subsequence caching.
-    if (m_paintLayer.layoutObject()->isLayoutPart() && toLayoutPart(m_paintLayer.layoutObject())->isThrottledFrameView())
-        return FullyPainted;
+    if (m_paintLayer.layoutObject()->isLayoutView() && toLayoutView(m_paintLayer.layoutObject())->frameView()->shouldThrottleRendering())
+        return result;
 
     // Ensure our lists are up-to-date.
     m_paintLayer.stackingNode()->updateLayerListsIfNeeded();
diff --git a/third_party/WebKit/Source/core/svg/SVGPathParser.cpp b/third_party/WebKit/Source/core/svg/SVGPathParser.cpp
index e688918..7a1b5c3 100644
--- a/third_party/WebKit/Source/core/svg/SVGPathParser.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGPathParser.cpp
@@ -297,18 +297,4 @@
     return true;
 }
 
-bool SVGPathParser::parseAndNormalizePath()
-{
-    SVGPathNormalizer normalizer(m_consumer);
-
-    while (m_source->hasMoreData()) {
-        PathSegmentData segment = m_source->parseSegment();
-        if (segment.command == PathSegUnknown)
-            return false;
-
-        normalizer.emitSegment(segment);
-    }
-    return true;
-}
-
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/svg/SVGPathParser.h b/third_party/WebKit/Source/core/svg/SVGPathParser.h
index f5f78d2..9274586 100644
--- a/third_party/WebKit/Source/core/svg/SVGPathParser.h
+++ b/third_party/WebKit/Source/core/svg/SVGPathParser.h
@@ -30,11 +30,6 @@
 
 namespace blink {
 
-enum PathParsingMode {
-    NormalizedParsing,
-    UnalteredParsing
-};
-
 class SVGPathConsumer;
 class SVGPathSource;
 
@@ -50,21 +45,18 @@
         ASSERT(m_consumer);
     }
 
-    bool parsePathDataFromSource(PathParsingMode pathParsingMode, bool checkForInitialMoveTo = true)
+    bool parsePathDataFromSource(bool checkForInitialMoveTo = true)
     {
         ASSERT(m_source);
         ASSERT(m_consumer);
         if (checkForInitialMoveTo && !initialCommandIsMoveTo())
             return false;
-        if (pathParsingMode == NormalizedParsing)
-            return parseAndNormalizePath();
         return parsePath();
     }
 
 private:
     bool initialCommandIsMoveTo();
     bool parsePath();
-    bool parseAndNormalizePath();
 
     SVGPathSource* m_source;
     SVGPathConsumer* m_consumer;
diff --git a/third_party/WebKit/Source/core/svg/SVGPathParserTest.cpp b/third_party/WebKit/Source/core/svg/SVGPathParserTest.cpp
index b81b52047..8cfa4de 100644
--- a/third_party/WebKit/Source/core/svg/SVGPathParserTest.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGPathParserTest.cpp
@@ -18,7 +18,7 @@
     SVGPathStringSource source(inputString);
     SVGPathStringBuilder builder;
     SVGPathParser parser(&source, &builder);
-    bool hadError = parser.parsePathDataFromSource(UnalteredParsing, true);
+    bool hadError = parser.parsePathDataFromSource();
     output = builder.result();
     // Coerce a null result to empty.
     if (output.isNull())
diff --git a/third_party/WebKit/Source/core/svg/SVGPathUtilities.cpp b/third_party/WebKit/Source/core/svg/SVGPathUtilities.cpp
index 259bf0e..1fde0eb 100644
--- a/third_party/WebKit/Source/core/svg/SVGPathUtilities.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGPathUtilities.cpp
@@ -36,7 +36,7 @@
     SVGPathBuilder builder(result);
     SVGPathStringSource source(d);
     SVGPathParser parser(&source, &builder);
-    return parser.parsePathDataFromSource(UnalteredParsing);
+    return parser.parsePathDataFromSource();
 }
 
 bool buildPathFromByteStream(const SVGPathByteStream& stream, Path& result)
@@ -47,7 +47,7 @@
     SVGPathBuilder builder(result);
     SVGPathByteStreamSource source(stream);
     SVGPathParser parser(&source, &builder);
-    return parser.parsePathDataFromSource(UnalteredParsing);
+    return parser.parsePathDataFromSource();
 }
 
 String buildStringFromByteStream(const SVGPathByteStream& stream)
@@ -58,7 +58,7 @@
     SVGPathStringBuilder builder;
     SVGPathByteStreamSource source(stream);
     SVGPathParser parser(&source, &builder);
-    parser.parsePathDataFromSource(UnalteredParsing);
+    parser.parsePathDataFromSource();
     return builder.result();
 }
 
@@ -74,7 +74,7 @@
     SVGPathByteStreamBuilder builder(result);
     SVGPathStringSource source(d);
     SVGPathParser parser(&source, &builder);
-    bool ok = parser.parsePathDataFromSource(UnalteredParsing);
+    bool ok = parser.parsePathDataFromSource();
     result.shrinkToFit();
     return ok;
 }
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.cpp
index c654111..cb7957d 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.cpp
@@ -27,6 +27,11 @@
 
 namespace blink {
 
+bool SVGImageForContainer::isTextureBacked()
+{
+    return m_image->isTextureBacked();
+}
+
 IntSize SVGImageForContainer::size() const
 {
     FloatSize scaledContainerSize(m_containerSize);
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
index ba6c83d7..08dfd5b 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
@@ -44,6 +44,7 @@
         return adoptRef(new SVGImageForContainer(image, containerSizeWithoutZoom, zoom, url));
     }
 
+    bool isTextureBacked() override;
     IntSize size() const override;
 
     bool usesContainerSize() const override { return m_image->usesContainerSize(); }
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
index 9277df92..a0f825c0 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
@@ -8,7 +8,9 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
+#include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/page/PageVisibilityState.h"
 #include "modules/bluetooth/BluetoothError.h"
 #include "modules/bluetooth/BluetoothGATTRemoteServer.h"
 #include "modules/bluetooth/BluetoothSupplement.h"
@@ -79,6 +81,13 @@
 
 ScriptPromise BluetoothDevice::connectGATT(ScriptState* scriptState)
 {
+    // TODO(ortuno): Allow connections when the tab is in the background.
+    // This is a short term solution instead of implementing a tab indicator
+    // for bluetooth connections.
+    // https://crbug.com/579746
+    if (!toDocument(scriptState->executionContext())->page()->isPageVisible()) {
+        return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, "Connection is only allowed while the page is visible. This is a temporary measure until we are able to effectively communicate to the user that a page is connected to a device."));
+    }
     WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState);
     if (!webbluetooth)
         return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError));
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp
index 862d9031..a6f0956 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp
@@ -8,6 +8,7 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptPromiseResolver.h"
 #include "core/dom/DOMException.h"
+#include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "modules/bluetooth/BluetoothError.h"
 #include "modules/bluetooth/BluetoothGATTService.h"
@@ -18,15 +19,56 @@
 
 namespace blink {
 
-BluetoothGATTRemoteServer::BluetoothGATTRemoteServer(PassOwnPtr<WebBluetoothGATTRemoteServer> webGATT)
-    : m_webGATT(webGATT)
+BluetoothGATTRemoteServer::BluetoothGATTRemoteServer(ExecutionContext* context, PassOwnPtr<WebBluetoothGATTRemoteServer> webGATT)
+    : ActiveDOMObject(context)
+    , PageLifecycleObserver(toDocument(context)->page())
+    , m_webGATT(webGATT)
 {
+    // See example in Source/platform/heap/ThreadState.h
+    ThreadState::current()->registerPreFinalizer(this);
 }
 
-BluetoothGATTRemoteServer* BluetoothGATTRemoteServer::take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothGATTRemoteServer> webGATT)
+BluetoothGATTRemoteServer* BluetoothGATTRemoteServer::take(ScriptPromiseResolver* resolver, PassOwnPtr<WebBluetoothGATTRemoteServer> webGATT)
 {
     ASSERT(webGATT);
-    return new BluetoothGATTRemoteServer(webGATT);
+    BluetoothGATTRemoteServer* server = new BluetoothGATTRemoteServer(resolver->executionContext(), webGATT);
+    if (!server->page()->isPageVisible()) {
+        server->disconnectIfConnected();
+    }
+    server->suspendIfNeeded();
+    return server;
+}
+
+void BluetoothGATTRemoteServer::dispose()
+{
+    disconnectIfConnected();
+}
+
+void BluetoothGATTRemoteServer::stop()
+{
+    disconnectIfConnected();
+}
+
+void BluetoothGATTRemoteServer::pageVisibilityChanged()
+{
+    if (!page()->isPageVisible()) {
+        disconnectIfConnected();
+    }
+}
+
+void BluetoothGATTRemoteServer::disconnectIfConnected()
+{
+    if (m_webGATT->connected) {
+        m_webGATT->connected = false;
+        WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(executionContext());
+        webbluetooth->disconnect(m_webGATT->deviceId);
+    }
+}
+
+DEFINE_TRACE(BluetoothGATTRemoteServer)
+{
+    ActiveDOMObject::trace(visitor);
+    PageLifecycleObserver::trace(visitor);
 }
 
 void BluetoothGATTRemoteServer::disconnect(ScriptState* scriptState)
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h
index 5cad459..87e65040 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h
@@ -7,6 +7,8 @@
 
 #include "bindings/core/v8/ScriptWrappable.h"
 #include "bindings/modules/v8/UnionTypesModules.h"
+#include "core/dom/ActiveDOMObject.h"
+#include "core/page/PageLifecycleObserver.h"
 #include "platform/heap/Heap.h"
 #include "public/platform/modules/bluetooth/WebBluetoothGATTRemoteServer.h"
 #include "wtf/OwnPtr.h"
@@ -27,17 +29,48 @@
 // CallbackPromiseAdapter class comments.
 class BluetoothGATTRemoteServer final
     : public GarbageCollectedFinalized<BluetoothGATTRemoteServer>
+    , public ActiveDOMObject
+    , public PageLifecycleObserver
     , public ScriptWrappable {
+    USING_PRE_FINALIZER(BluetoothGATTRemoteServer, dispose);
     DEFINE_WRAPPERTYPEINFO();
+    WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(BluetoothGATTRemoteServer);
 public:
-    BluetoothGATTRemoteServer(PassOwnPtr<WebBluetoothGATTRemoteServer>);
+    BluetoothGATTRemoteServer(ExecutionContext*, PassOwnPtr<WebBluetoothGATTRemoteServer>);
 
     // Interface required by CallbackPromiseAdapter:
     using WebType = OwnPtr<WebBluetoothGATTRemoteServer>;
     static BluetoothGATTRemoteServer* take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothGATTRemoteServer>);
 
+    // We should disconnect from the device in all of the following cases:
+    // 1. When the object gets GarbageCollected e.g. it went out of scope.
+    // dispose() is called in this case.
+    // 2. When the parent document gets detached e.g. reloading a page.
+    // stop() is called in this case.
+    // 3. For now (https://crbug.com/579746), when the tab is no longer in the
+    // foreground e.g. change tabs.
+    // pageVisibilityChanged() is called in this case.
+    // TODO(ortuno): Users should be able to turn on notifications listen for
+    // events on navigator.bluetooth and still remain connected even if the
+    // BluetoothGATTRemoteServer object is garbage collected.
+    // TODO(ortuno): Allow connections when the tab is in the background.
+    // This is a short term solution instead of implementing a tab indicator
+    // for bluetooth connections.
+
+    // USING_PRE_FINALIZER interface.
+    // Called before the object gets garbage collected.
+    void dispose();
+
+    // ActiveDOMObject interface.
+    void stop() override;
+
+    // PageLifecycleObserver interface.
+    void pageVisibilityChanged() override;
+
+    void disconnectIfConnected();
+
     // Interface required by Garbage Collectoin:
-    DEFINE_INLINE_TRACE() { }
+    DECLARE_VIRTUAL_TRACE();
 
     // IDL exposed interface:
     bool connected() { return m_webGATT->connected; }
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl
index 9c878fdb..35e437a 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl
@@ -8,6 +8,7 @@
 
 [
     GarbageCollected,
+    ActiveDOMObject,
     RuntimeEnabled=WebBluetooth,
 ] interface BluetoothGATTRemoteServer
 // Implement ServiceEventHandlers interface: http://crbug.com/421670
diff --git a/third_party/WebKit/Source/modules/mediasession/HTMLMediaElementMediaSession.h b/third_party/WebKit/Source/modules/mediasession/HTMLMediaElementMediaSession.h
index f847be8..b4cd9e4 100644
--- a/third_party/WebKit/Source/modules/mediasession/HTMLMediaElementMediaSession.h
+++ b/third_party/WebKit/Source/modules/mediasession/HTMLMediaElementMediaSession.h
@@ -15,7 +15,7 @@
 
 // A supplement to HTMLMediaElement responsible for the integration of
 // MediaSession with HTMLMediaElement.
-class HTMLMediaElementMediaSession final : public NoBaseWillBeGarbageCollected<HTMLMediaElementMediaSession>, public WillBeHeapSupplement<HTMLMediaElement> {
+class MODULES_EXPORT HTMLMediaElementMediaSession final : public NoBaseWillBeGarbageCollected<HTMLMediaElementMediaSession>, public WillBeHeapSupplement<HTMLMediaElement> {
     WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(HTMLMediaElementMediaSession);
     USING_FAST_MALLOC_WILL_BE_REMOVED(HTMLMediaElementMediaSession);
 public:
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaSession.h b/third_party/WebKit/Source/modules/mediasession/MediaSession.h
index 267f059..1ec2837 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaSession.h
+++ b/third_party/WebKit/Source/modules/mediasession/MediaSession.h
@@ -24,6 +24,8 @@
 public:
     static MediaSession* create(ExecutionContext*, ExceptionState&);
 
+    WebMediaSession* webMediaSession() { return m_webMediaSession.get(); }
+
     ScriptPromise activate(ScriptState*);
     ScriptPromise deactivate(ScriptState*);
 
diff --git a/third_party/WebKit/Source/modules/notifications/Notification.idl b/third_party/WebKit/Source/modules/notifications/Notification.idl
index b985e3db..24310c14 100644
--- a/third_party/WebKit/Source/modules/notifications/Notification.idl
+++ b/third_party/WebKit/Source/modules/notifications/Notification.idl
@@ -65,7 +65,7 @@
     readonly attribute DOMString lang;
     readonly attribute DOMString body;
     readonly attribute DOMString tag;
-    readonly attribute DOMString icon;
+    readonly attribute USVString icon;
 
     [RuntimeEnabled=NotificationExperimental] readonly attribute sequence<unsigned long>? vibrate;
     readonly attribute boolean silent;
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationOptions.idl b/third_party/WebKit/Source/modules/notifications/NotificationOptions.idl
index feba5b0e..67c36db 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationOptions.idl
+++ b/third_party/WebKit/Source/modules/notifications/NotificationOptions.idl
@@ -15,7 +15,7 @@
     DOMString lang = "";
     DOMString body = "";
     DOMString tag = "";
-    DOMString icon;
+    USVString icon;
     // TODO(sh919.park): vibrate should be ([Clamp] unsigned long or sequence<unsigned long>)
     (unsigned long or sequence<unsigned long>) vibrate;
     boolean silent = false;
diff --git a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
index 167b3807..00a0aef 100644
--- a/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
+++ b/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp
@@ -58,6 +58,7 @@
 #include "modules/device_orientation/DeviceOrientationController.h"
 #include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
 #include "modules/gamepad/NavigatorGamepad.h"
+#include "modules/mediasession/HTMLMediaElementMediaSession.h"
 #include "modules/mediasession/MediaSession.h"
 #include "modules/serviceworkers/NavigatorServiceWorker.h"
 #include "modules/storage/DOMWindowStorageController.h"
@@ -818,11 +819,15 @@
     if (!webFrame || !webFrame->client())
         return nullptr;
 
+    WebMediaSession* webMediaSession = nullptr;
+    if (MediaSession* mediaSession = HTMLMediaElementMediaSession::session(htmlMediaElement))
+        webMediaSession = mediaSession->webMediaSession();
+
     HTMLMediaElementEncryptedMedia& encryptedMedia = HTMLMediaElementEncryptedMedia::from(htmlMediaElement);
     WebString sinkId(HTMLMediaElementAudioOutputDevice::sinkId(htmlMediaElement));
     return adoptPtr(webFrame->client()->createMediaPlayer(webFrame, loadType, url,
         client, &encryptedMedia,
-        encryptedMedia.contentDecryptionModule(), sinkId));
+        encryptedMedia.contentDecryptionModule(), sinkId, webMediaSession));
 }
 
 PassOwnPtr<WebMediaSession> FrameLoaderClientImpl::createWebMediaSession()
diff --git a/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
index beca4dd..3abba53 100644
--- a/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
@@ -211,7 +211,7 @@
     frameElement->contentDocument()->documentElement()->setAttribute(styleAttr, "background: green");
     EXPECT_FALSE(compositor().needsAnimate());
 
-    // Moving the frame back on screen to unthrottle it.
+    // Move the frame back on screen to unthrottle it.
     frameElement->setAttribute(styleAttr, "");
     EXPECT_TRUE(compositor().needsAnimate());
 
@@ -251,6 +251,65 @@
     EXPECT_EQ(50, divElement->clientWidth());
 }
 
+TEST_F(FrameThrottlingTest, UnthrottlingTriggersRepaint)
+{
+    // Create a hidden frame which is throttled.
+    SimRequest mainResource("https://example.com/", "text/html");
+    SimRequest frameResource("https://example.com/iframe.html", "text/html");
+
+    loadURL("https://example.com/");
+    mainResource.complete("<iframe id=frame sandbox src=iframe.html></iframe>");
+    frameResource.complete("<style> html { background: green; } </style>");
+
+    // Move the frame offscreen to throttle it.
+    auto* frameElement = toHTMLIFrameElement(document().getElementById("frame"));
+    frameElement->setAttribute(styleAttr, "transform: translateY(480px)");
+    EXPECT_FALSE(frameElement->contentDocument()->view()->shouldThrottleRendering());
+    compositeFrame();
+    EXPECT_TRUE(frameElement->contentDocument()->view()->shouldThrottleRendering());
+
+    // Scroll down to unthrottle the frame. The first frame we composite after
+    // scrolling won't contain the frame yet, but will schedule another repaint.
+    webView().mainFrameImpl()->frameView()->setScrollPosition(DoublePoint(0, 480), ProgrammaticScroll);
+    auto displayItems = compositeFrame();
+    EXPECT_FALSE(displayItems.contains(SimCanvas::Rect, "green"));
+
+    // Now the frame contents should be visible again.
+    auto displayItems2 = compositeFrame();
+    EXPECT_TRUE(displayItems2.contains(SimCanvas::Rect, "green"));
+}
+
+TEST_F(FrameThrottlingTest, ChangeStyleInThrottledFrame)
+{
+    // Create a hidden frame which is throttled.
+    SimRequest mainResource("https://example.com/", "text/html");
+    SimRequest frameResource("https://example.com/iframe.html", "text/html");
+
+    loadURL("https://example.com/");
+    mainResource.complete("<iframe id=frame sandbox src=iframe.html></iframe>");
+    frameResource.complete("<style> html { background: red; } </style>");
+
+    // Move the frame offscreen to throttle it.
+    auto* frameElement = toHTMLIFrameElement(document().getElementById("frame"));
+    frameElement->setAttribute(styleAttr, "transform: translateY(480px)");
+    EXPECT_FALSE(frameElement->contentDocument()->view()->shouldThrottleRendering());
+    compositeFrame();
+    EXPECT_TRUE(frameElement->contentDocument()->view()->shouldThrottleRendering());
+
+    // Change the background color of the frame's contents from red to green.
+    frameElement->contentDocument()->body()->setAttribute(styleAttr, "background: green");
+
+    // Scroll down to unthrottle the frame.
+    webView().mainFrameImpl()->frameView()->setScrollPosition(DoublePoint(0, 480), ProgrammaticScroll);
+    auto displayItems = compositeFrame();
+    EXPECT_FALSE(displayItems.contains(SimCanvas::Rect, "red"));
+    EXPECT_FALSE(displayItems.contains(SimCanvas::Rect, "green"));
+
+    // Make sure the new style shows up instead of the old one.
+    auto displayItems2 = compositeFrame();
+    EXPECT_TRUE(displayItems2.contains(SimCanvas::Rect, "green"));
+}
+
 TEST(RemoteFrameThrottlingTest, ThrottledLocalRoot)
 {
     FrameTestHelpers::TestWebViewClient viewClient;
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index a57f5d7..86c77e29 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -113,7 +113,7 @@
 
     // May return null.
     // WebContentDecryptionModule* may be null if one has not yet been set.
-    virtual WebMediaPlayer* createMediaPlayer(WebLocalFrame*, WebMediaPlayer::LoadType, const WebURL&, WebMediaPlayerClient*, WebMediaPlayerEncryptedMediaClient*, WebContentDecryptionModule*, const WebString& sinkId) { return 0; }
+    virtual WebMediaPlayer* createMediaPlayer(WebLocalFrame*, WebMediaPlayer::LoadType, const WebURL&, WebMediaPlayerClient*, WebMediaPlayerEncryptedMediaClient*, WebContentDecryptionModule*, const WebString& sinkId, WebMediaSession*) { return 0; }
 
     // May return null.
     virtual WebMediaSession* createMediaSession() { return 0; }
diff --git a/tools/android/loading/analyze.py b/tools/android/loading/analyze.py
index 27294d38..5f15fe23 100755
--- a/tools/android/loading/analyze.py
+++ b/tools/android/loading/analyze.py
@@ -24,6 +24,7 @@
 import devil_chromium
 from pylib import constants
 
+import content_classification_lens
 import device_setup
 import loading_model
 import loading_trace
@@ -147,10 +148,14 @@
 
 # TODO(mattcary): it would be nice to refactor so the --noads flag gets dealt
 # with here.
-def _ProcessRequests(filename):
+def _ProcessRequests(filename, ad_rules_filename='',
+                     tracking_rules_filename=''):
   with open(filename) as f:
-    return loading_model.ResourceGraph(
-        loading_trace.LoadingTrace.FromJsonDict(json.load(f)))
+    trace = loading_trace.LoadingTrace.FromJsonDict(json.load(f))
+    content_lens = (
+        content_classification_lens.ContentClassificationLens.WithRulesFiles(
+            trace, ad_rules_filename, tracking_rules_filename))
+    return loading_model.ResourceGraph(trace, content_lens)
 
 
 def InvalidCommand(cmd):
@@ -185,8 +190,11 @@
   parser.add_argument('--eog', action='store_true')
   parser.add_argument('--highlight')
   parser.add_argument('--noads', action='store_true')
+  parser.add_argument('--ad_rules', default='')
+  parser.add_argument('--tracking_rules', default='')
   args = parser.parse_args(arg_str)
-  graph = _ProcessRequests(args.request_json)
+  graph = _ProcessRequests(
+      args.request_json, args.ad_rules, args.tracking_rules)
   if args.noads:
     graph.Set(node_filter=graph.FilterAds)
   tmp = tempfile.NamedTemporaryFile()
diff --git a/tools/android/loading/content_classification_lens.py b/tools/android/loading/content_classification_lens.py
new file mode 100644
index 0000000..2e8f0bd
--- /dev/null
+++ b/tools/android/loading/content_classification_lens.py
@@ -0,0 +1,118 @@
+# 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.
+
+"""Labels requests according to the type of content they represent."""
+
+import adblockparser # Available on PyPI, through pip.
+import collections
+import os
+
+import loading_trace
+import request_track
+
+
+class ContentClassificationLens(object):
+  """Associates requests and frames with the type of content they represent."""
+  def __init__(self, trace, ad_rules, tracking_rules):
+    """Initializes an instance of ContentClassificationLens.
+
+    Args:
+      trace: (LoadingTrace) loading trace.
+      ad_rules: ([str]) List of Adblock+ compatible rules used to classify ads.
+      tracking_rules: ([str]) List of Adblock+ compatible rules used to
+                      classify tracking and analytics.
+    """
+    self._trace = trace
+    self._requests = trace.request_track.GetEvents()
+    self._main_frame_id = trace.page_track.GetEvents()[0]['frame_id']
+    self._frame_to_requests = collections.defaultdict(list)
+    self._ad_requests = set()
+    self._tracking_requests = set()
+    self._ad_matcher = _RulesMatcher(ad_rules, True)
+    self._tracking_matcher = _RulesMatcher(tracking_rules, True)
+    self._GroupRequestsByFrameId()
+    self._LabelRequests()
+
+  def IsAdRequest(self, request):
+    """Returns True iff the request matches one of the ad_rules."""
+    return request.request_id in self._ad_requests
+
+  def IsTrackingRequest(self, request):
+    """Returns True iff the request matches one of the tracking_rules."""
+    return request.request_id in self._tracking_requests
+
+  def IsAdFrame(self, frame_id, ratio):
+    """A Frame is an Ad frame if more than |ratio| of its requests are
+    ad-related, and is not the main frame."""
+    if frame_id == self._main_frame_id:
+      return False
+    ad_requests_count = sum(r in self._ad_requests
+                            for r in self._frame_to_requests[frame_id])
+    frame_requests_count = len(self._frame_to_requests[frame_id])
+    return (float(ad_requests_count) / frame_requests_count) > ratio
+
+  @classmethod
+  def WithRulesFiles(cls, trace, ad_rules_filename, tracking_rules_filename):
+    """Returns an instance of ContentClassificationLens with the rules read
+    from files.
+    """
+    ad_rules = []
+    tracking_rules = []
+    if os.path.exists(ad_rules_filename):
+      ad_rules = open(ad_rules_filename, 'r').readlines()
+    if os.path.exists(tracking_rules_filename):
+      tracking_rules = open(tracking_rules_filename, 'r').readlines()
+    return ContentClassificationLens(trace, ad_rules, tracking_rules)
+
+  def _GroupRequestsByFrameId(self):
+    for request in self._requests:
+      frame_id = request.frame_id
+      self._frame_to_requests[frame_id].append(request.request_id)
+
+  def _LabelRequests(self):
+    for request in self._requests:
+      request_id = request.request_id
+      if self._ad_matcher.Matches(request):
+        self._ad_requests.add(request_id)
+      if self._tracking_matcher.Matches(request):
+        self._tracking_requests.add(request_id)
+
+
+class _RulesMatcher(object):
+  """Matches requests with rules in Adblock+ format."""
+  _WHITELIST_PREFIX = '@@'
+  _RESOURCE_TYPE_TO_OPTIONS_KEY = {
+      'Script': 'script', 'Stylesheet': 'stylesheet', 'Image': 'image',
+      'XHR': 'xmlhttprequest'}
+  def __init__(self, rules, no_whitelist):
+    """Initializes an instance of _RulesMatcher.
+
+    Args:
+      rules: ([str]) list of rules.
+      no_whitelist: (bool) Whether the whitelisting rules should be ignored.
+    """
+    self._rules = self._FilterRules(rules, no_whitelist)
+    self._matcher = adblockparser.AdblockRules(self._rules)
+
+  def Matches(self, request):
+    """Returns whether a request matches one of the rules."""
+    url = request.url
+    return self._matcher.should_block(url, self._GetOptions(request))
+
+  @classmethod
+  def _GetOptions(cls, request):
+    options = {}
+    resource_type = request.resource_type
+    option = cls._RESOURCE_TYPE_TO_OPTIONS_KEY.get(resource_type)
+    if option:
+      options[option] = True
+    return options
+
+  @classmethod
+  def _FilterRules(cls, rules, no_whitelist):
+    if not no_whitelist:
+      return rules
+    else:
+      return [rule for rule in rules
+              if not rule.startswith(cls._WHITELIST_PREFIX)]
diff --git a/tools/android/loading/content_classification_lens_unittest.py b/tools/android/loading/content_classification_lens_unittest.py
new file mode 100644
index 0000000..cca7d52b
--- /dev/null
+++ b/tools/android/loading/content_classification_lens_unittest.py
@@ -0,0 +1,85 @@
+# 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.
+
+import copy
+import unittest
+
+from content_classification_lens import (ContentClassificationLens,
+                                         _RulesMatcher)
+from request_track import (Request, TimingFromDict)
+import test_utils
+
+
+class ContentClassificationLensTestCase(unittest.TestCase):
+  _REQUEST = Request.FromJsonDict({'url': 'http://bla.com',
+                                   'request_id': '1234.1',
+                                   'frame_id': '123.1',
+                                   'initiator': {'type': 'other'},
+                                   'timestamp': 2,
+                                   'timing': TimingFromDict({})})
+  _MAIN_FRAME_ID = '123.1'
+  _PAGE_EVENTS = [{'method': 'Page.frameStartedLoading',
+                   'frame_id': _MAIN_FRAME_ID},
+                  {'method': 'Page.frameAttached',
+                   'frame_id': '123.13', 'parent_frame_id': _MAIN_FRAME_ID}]
+  _RULES = ['bla.com']
+
+  def testAdRequest(self):
+    trace = test_utils.LoadingTraceFromEvents(
+        [self._REQUEST], self._PAGE_EVENTS)
+    lens = ContentClassificationLens(trace, self._RULES, [])
+    self.assertTrue(lens.IsAdRequest(self._REQUEST))
+    self.assertFalse(lens.IsTrackingRequest(self._REQUEST))
+
+  def testTrackingRequest(self):
+    trace = test_utils.LoadingTraceFromEvents(
+        [self._REQUEST], self._PAGE_EVENTS)
+    lens = ContentClassificationLens(trace, [], self._RULES)
+    self.assertFalse(lens.IsAdRequest(self._REQUEST))
+    self.assertTrue(lens.IsTrackingRequest(self._REQUEST))
+
+  def testMainFrameIsNotAdFrame(self):
+    trace = test_utils.LoadingTraceFromEvents(
+        [self._REQUEST] * 10, self._PAGE_EVENTS)
+    lens = ContentClassificationLens(trace, self._RULES, [])
+    self.assertFalse(lens.IsAdFrame(self._MAIN_FRAME_ID, .5))
+
+  def testAdFrame(self):
+    request = self._REQUEST
+    request.frame_id = '123.123'
+    trace = test_utils.LoadingTraceFromEvents(
+        [request] * 10 + [self._REQUEST] * 5, self._PAGE_EVENTS)
+    lens = ContentClassificationLens(trace, self._RULES, [])
+    self.assertTrue(lens.IsAdFrame(request.frame_id, .5))
+
+
+class _MatcherTestCase(unittest.TestCase):
+  _RULES_WITH_WHITELIST = ['/thisisanad.', '@@myadvertisingdomain.com/*',
+                           '@@||www.mydomain.com/ads/$elemhide']
+  _SCRIPT_RULE = 'domainwithscripts.com/*$script'
+  _SCRIPT_REQUEST = Request.FromJsonDict(
+      {'url': 'http://domainwithscripts.com/bla.js',
+       'resource_type': 'Script',
+       'request_id': '1234.1',
+       'frame_id': '123.1',
+       'initiator': {'type': 'other'},
+       'timestamp': 2,
+       'timing': TimingFromDict({})})
+
+  def testRemovesWhitelistRules(self):
+    matcher = _RulesMatcher(self._RULES_WITH_WHITELIST, False)
+    self.assertEquals(3, len(matcher._rules))
+    matcher = _RulesMatcher(self._RULES_WITH_WHITELIST, True)
+    self.assertEquals(1, len(matcher._rules))
+
+  def testScriptRule(self):
+    matcher = _RulesMatcher([self._SCRIPT_RULE], False)
+    request = copy.deepcopy(self._SCRIPT_REQUEST)
+    request.resource_type = 'Stylesheet'
+    self.assertFalse(matcher.Matches(request))
+    self.assertTrue(matcher.Matches(self._SCRIPT_REQUEST))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/android/loading/loading_model.py b/tools/android/loading/loading_model.py
index 155da1dd..b6aeca6 100644
--- a/tools/android/loading/loading_model.py
+++ b/tools/android/loading/loading_model.py
@@ -30,14 +30,17 @@
   Set parameters:
     cache_all: if true, assume zero loading time for all resources.
   """
-  def __init__(self, trace):
+  def __init__(self, trace, content_lens=None):
     """Create from a LoadingTrace (or json of a trace).
 
     Args:
       trace: (LoadingTrace/JSON) Loading trace or JSON of a trace.
+      content_lens: (ContentClassificationLens) Lens used to annotate the
+                    nodes, or None.
     """
     if type(trace) == dict:
       trace = loading_trace.LoadingTrace.FromJsonDict(trace)
+    self._content_lens = content_lens
     self._BuildDag(trace)
     self._global_start = min([n.StartTime() for n in self._node_info])
     # Sort before splitting children so that we can correctly dectect if a
@@ -244,7 +247,9 @@
       for s in n.Successors():
         style = 'color = orange'
         annotations = self._EdgeAnnotation(n, s)
-        if 'parser' in annotations:
+        if 'redirect' in annotations:
+          style = 'color = black'
+        elif 'parser' in annotations:
           style = 'color = red'
         elif 'stack' in annotations:
           style = 'color = blue'
@@ -339,6 +344,8 @@
         request: The request associated with this node.
       """
       self._request = request
+      self._is_ad = False
+      self._is_tracking = False
       self._node = node
       self._edge_costs = {}
       self._edge_annotations = {}
@@ -357,6 +364,21 @@
     def Index(self):
       return self._node.Index()
 
+    def SetRequestContent(self, is_ad, is_tracking):
+      """Sets the kind of content the request relates to.
+
+      Args:
+        is_ad: (bool) Whether the request is an Ad.
+        is_tracking: (bool) Whether the request is related to tracking.
+      """
+      (self._is_ad, self._is_tracking) = (is_ad, is_tracking)
+
+    def IsAd(self):
+      return self._is_ad
+
+    def IsTracking(self):
+      return self._is_tracking
+
     def Request(self):
       return self._request
 
@@ -481,6 +503,9 @@
       index_by_request[request] = next_index
       node = dag.Node(next_index)
       node_info = self._NodeInfo(node, request)
+      if self._content_lens:
+        node.SetRequestContent(self._content_lens.IsAdRequest(request),
+                               self._content_lens.IsTrackingRequest(request))
       self._nodes.append(node)
       self._node_info.append(node_info)
 
@@ -606,6 +631,8 @@
         if fragment in node_info.Url():
           styles.append('dotted')
           break
+    if node_info.IsAd() or node_info.IsTracking():
+      styles += ['bold', 'diagonals']
     return ('%d [label = "%s\\n%.2f->%.2f (%.2f)"; style = "%s"; '
             'fillcolor = %s; shape = %s];\n'
             % (index, node_info.ShortName(),
diff --git a/tools/android/loading/loading_model_unittest.py b/tools/android/loading/loading_model_unittest.py
index 7033cef..b866136 100644
--- a/tools/android/loading/loading_model_unittest.py
+++ b/tools/android/loading/loading_model_unittest.py
@@ -72,7 +72,9 @@
                        self.MakeParserRequest(1, 0, 102, 103).ToJsonDict(),
                        self.MakeParserRequest(2, 0, 102, 103).ToJsonDict(),
                        self.MakeParserRequest(3, 2, 104, 105).ToJsonDict()],
-            'metadata': { 'duplicates_count' : 0 }},
+            'metadata': {
+                request_track.RequestTrack._DUPLICATES_KEY: 0,
+                request_track.RequestTrack._INCONSISTENT_INITIATORS_KEY: 0}},
          'url': 'foo.com',
          'tracing_track': {'events': []},
          'page_track': {'events': []},
diff --git a/tools/android/loading/request_dependencies_lens.py b/tools/android/loading/request_dependencies_lens.py
index 47ea3af5..81289572 100644
--- a/tools/android/loading/request_dependencies_lens.py
+++ b/tools/android/loading/request_dependencies_lens.py
@@ -64,8 +64,7 @@
     """
     reason = request.initiator['type']
     assert reason in request_track.Request.INITIATORS
-    # Redirect suffixes are added in RequestTrack.
-    if request.request_id.endswith(request_track.RequestTrack.REDIRECT_SUFFIX):
+    if reason == 'redirect':
       return self._GetInitiatingRequestRedirect(request)
     elif reason == 'parser':
       return self._GetInitiatingRequestParser(request)
@@ -76,14 +75,11 @@
       return self._GetInitiatingRequestOther(request)
 
   def _GetInitiatingRequestRedirect(self, request):
-    request_id = request.request_id[:request.request_id.index(
-        request_track.RequestTrack.REDIRECT_SUFFIX)]
-    assert request_id in self._requests_by_id
-    dependent_request = self._requests_by_id[request_id]
-    assert request.timing.request_time < \
-        dependent_request.timing.request_time, '.\n'.join(
-            [str(request), str(dependent_request)])
-    return (request, dependent_request, 'redirect')
+    assert request_track.Request.INITIATING_REQUEST in request.initiator
+    initiating_request_id = request.initiator[
+        request_track.Request.INITIATING_REQUEST]
+    assert initiating_request_id in self._requests_by_id
+    return (self._requests_by_id[initiating_request_id], request, 'redirect')
 
   def _GetInitiatingRequestParser(self, request):
     url = request.initiator['url']
diff --git a/tools/android/loading/request_dependencies_lens_unittest.py b/tools/android/loading/request_dependencies_lens_unittest.py
index 4fc5d907..0ab7403b 100644
--- a/tools/android/loading/request_dependencies_lens_unittest.py
+++ b/tools/android/loading/request_dependencies_lens_unittest.py
@@ -13,9 +13,17 @@
 
 class RequestDependencyLensTestCase(unittest.TestCase):
   _REDIRECT_REQUEST = Request.FromJsonDict(
-      {'url': 'http://bla.com', 'request_id': '1234.1.redirect',
+      {'url': 'http://bla.com', 'request_id': '1234.redirect.1',
        'initiator': {'type': 'other'},
        'timestamp': 1, 'timing': TimingFromDict({})})
+  _REDIRECTED_REQUEST = Request.FromJsonDict({
+      'url': 'http://bla.com',
+      'request_id': '1234.1',
+      'frame_id': '123.1',
+      'initiator': {'type': 'redirect',
+                    'initiating_request': '1234.redirect.1'},
+      'timestamp': 2,
+      'timing': TimingFromDict({})})
   _REQUEST = Request.FromJsonDict({'url': 'http://bla.com',
                                    'request_id': '1234.1',
                                    'frame_id': '123.1',
@@ -55,7 +63,7 @@
 
   def testRedirectDependency(self):
     loading_trace = test_utils.LoadingTraceFromEvents(
-        [self._REDIRECT_REQUEST, self._REQUEST])
+        [self._REDIRECT_REQUEST, self._REDIRECTED_REQUEST])
     request_dependencies_lens = RequestDependencyLens(loading_trace)
     deps = request_dependencies_lens.GetRequestDependencies()
     self.assertEquals(1, len(deps))
@@ -86,7 +94,7 @@
 
   def testSeveralDependencies(self):
     loading_trace = test_utils.LoadingTraceFromEvents(
-        [self._REDIRECT_REQUEST, self._REQUEST, self._JS_REQUEST,
+        [self._REDIRECT_REQUEST, self._REDIRECTED_REQUEST, self._JS_REQUEST,
          self._JS_REQUEST_2])
     request_dependencies_lens = RequestDependencyLens(loading_trace)
     deps = request_dependencies_lens.GetRequestDependencies()
diff --git a/tools/android/loading/request_track.py b/tools/android/loading/request_track.py
index 60a54e5..566fb3c3 100644
--- a/tools/android/loading/request_track.py
+++ b/tools/android/loading/request_track.py
@@ -46,7 +46,7 @@
   third_party/WebKit/Source/devtools/protocol.json.
 
   Fields:
-    request_id: (str) unique request ID. Postfixed with REDIRECT_SUFFIX for
+    request_id: (str) unique request ID. Postfixed with _REDIRECT_SUFFIX for
                 redirects.
     frame_id: (str) unique frame identifier.
     loader_id: (str) unique frame identifier.
@@ -77,7 +77,9 @@
   RESOURCE_TYPES = ('Document', 'Stylesheet', 'Image', 'Media', 'Font',
                     'Script', 'TextTrack', 'XHR', 'Fetch', 'EventSource',
                     'WebSocket', 'Manifest', 'Other')
-  INITIATORS = ('parser', 'script', 'other')
+  INITIATORS = ('parser', 'script', 'other', 'redirect')
+  INITIATING_REQUEST = 'initiating_request'
+  ORIGINAL_INITIATOR = 'original_initiator'
   def __init__(self):
     self.request_id = None
     self.frame_id = None
@@ -172,7 +174,7 @@
 
 class RequestTrack(devtools_monitor.Track):
   """Aggregates request data."""
-  REDIRECT_SUFFIX = '.redirect'
+  _REDIRECT_SUFFIX = '.redirect'
   # Request status
   _STATUS_SENT = 0
   _STATUS_RESPONSE = 1
@@ -183,12 +185,14 @@
   _EVENTS_KEY = 'events'
   _METADATA_KEY = 'metadata'
   _DUPLICATES_KEY = 'duplicates_count'
+  _INCONSISTENT_INITIATORS_KEY = 'inconsistent_initiators'
   def __init__(self, connection):
     super(RequestTrack, self).__init__(connection)
     self._connection = connection
     self._requests = []
     self._requests_in_flight = {}  # requestId -> (request, status)
     self._completed_requests_by_id = {}
+    self._redirects_count_by_id = collections.defaultdict(int)
     if connection:  # Optional for testing.
       for method in RequestTrack._METHOD_TO_HANDLER:
         self._connection.RegisterListener(method, self)
@@ -196,6 +200,7 @@
     # detect this.
     self._request_id_to_response_received = {}
     self.duplicates_count = 0
+    self.inconsistent_initiators_count = 0
 
   def Handle(self, method, msg):
     assert method in RequestTrack._METHOD_TO_HANDLER
@@ -214,7 +219,10 @@
       logging.warning('Requests in flight, will be ignored in the dump')
     return {self._EVENTS_KEY: [
         request.ToJsonDict() for request in self._requests],
-            self._METADATA_KEY: {self._DUPLICATES_KEY: self.duplicates_count}}
+            self._METADATA_KEY: {
+                self._DUPLICATES_KEY: self.duplicates_count,
+                self._INCONSISTENT_INITIATORS_KEY:
+                self.inconsistent_initiators_count}}
 
   @classmethod
   def FromJsonDict(cls, json_dict):
@@ -224,14 +232,18 @@
     requests = [Request.FromJsonDict(request)
                 for request in json_dict[cls._EVENTS_KEY]]
     result._requests = requests
-    result.duplicates_count = json_dict[cls._METADATA_KEY][cls._DUPLICATES_KEY]
+    metadata = json_dict[cls._METADATA_KEY]
+    result.duplicates_count = metadata.get(cls._DUPLICATES_KEY, 0)
+    result.inconsistent_initiators_count = metadata.get(
+        cls._INCONSISTENT_INITIATORS_KEY, 0)
     return result
 
   def _RequestWillBeSent(self, request_id, params):
     # Several "requestWillBeSent" events can be dispatched in a row in the case
     # of redirects.
+    redirect_initiator = None
     if request_id in self._requests_in_flight:
-      self._HandleRedirect(request_id, params)
+      redirect_initiator = self._HandleRedirect(request_id, params)
     assert (request_id not in self._requests_in_flight
             and request_id not in self._completed_requests_by_id)
     r = Request()
@@ -247,6 +259,16 @@
                      ('headers', 'headers'),
                      ('initialPriority', 'initial_priority')))
     r.resource_type = params.get('type', 'Other')
+    if redirect_initiator:
+      original_initiator = r.initiator
+      r.initiator = redirect_initiator
+      r.initiator[Request.ORIGINAL_INITIATOR] = original_initiator
+      initiating_request = self._completed_requests_by_id[
+          redirect_initiator[Request.INITIATING_REQUEST]]
+      initiating_initiator = initiating_request.initiator.get(
+          Request.ORIGINAL_INITIATOR, initiating_request.initiator)
+      if initiating_initiator != original_initiator:
+        self.inconsistent_initiators_count += 1
     self._requests_in_flight[request_id] = (r, RequestTrack._STATUS_SENT)
 
   def _HandleRedirect(self, request_id, params):
@@ -256,15 +278,23 @@
     # one. Finalize the first request.
     assert 'redirectResponse' in params
     redirect_response = params['redirectResponse']
+
     _CopyFromDictToObject(redirect_response, r,
                           (('headers', 'response_headers'),
                            ('encodedDataLength', 'encoded_data_length'),
                            ('fromDiskCache', 'from_disk_cache')))
     r.timing = TimingFromDict(redirect_response['timing'])
-    r.request_id = request_id + self.REDIRECT_SUFFIX
+
+    redirect_index = self._redirects_count_by_id[request_id]
+    self._redirects_count_by_id[request_id] += 1
+    r.request_id = '%s%s.%d' % (request_id, self._REDIRECT_SUFFIX,
+                                 redirect_index + 1)
+    initiator = {
+        'type': 'redirect', Request.INITIATING_REQUEST: r.request_id}
     self._requests_in_flight[r.request_id] = (r, RequestTrack._STATUS_FINISHED)
     del self._requests_in_flight[request_id]
     self._FinalizeRequest(r.request_id)
+    return initiator
 
   def _RequestServedFromCache(self, request_id, _):
     assert request_id in self._requests_in_flight
diff --git a/tools/android/loading/request_track_unittest.py b/tools/android/loading/request_track_unittest.py
index fde7399..e742f60 100644
--- a/tools/android/loading/request_track_unittest.py
+++ b/tools/android/loading/request_track_unittest.py
@@ -195,7 +195,47 @@
     self.assertEquals(1, len(self.request_track.GetEvents()))
     redirect_request = self.request_track.GetEvents()[0]
     self.assertTrue(redirect_request.request_id.endswith(
-        RequestTrack.REDIRECT_SUFFIX))
+        RequestTrack._REDIRECT_SUFFIX + '.1'))
+    request = self.request_track._requests_in_flight.values()[0][0]
+    self.assertEquals('redirect', request.initiator['type'])
+    self.assertEquals(
+        redirect_request.request_id,
+        request.initiator[Request.INITIATING_REQUEST])
+    self.assertEquals(0, self.request_track.inconsistent_initiators_count)
+
+  def testMultipleRedirects(self):
+    self.request_track.Handle('Network.requestWillBeSent',
+                              RequestTrackTestCase._REQUEST_WILL_BE_SENT)
+    self.request_track.Handle('Network.requestWillBeSent',
+                              RequestTrackTestCase._REDIRECT)
+    self.request_track.Handle('Network.requestWillBeSent',
+                              RequestTrackTestCase._REDIRECT)
+    self.assertEquals(1, len(self.request_track._requests_in_flight))
+    self.assertEquals(2, len(self.request_track.GetEvents()))
+    first_redirect_request = self.request_track.GetEvents()[0]
+    self.assertTrue(first_redirect_request.request_id.endswith(
+        RequestTrack._REDIRECT_SUFFIX + '.1'))
+    second_redirect_request = self.request_track.GetEvents()[1]
+    self.assertTrue(second_redirect_request.request_id.endswith(
+        RequestTrack._REDIRECT_SUFFIX + '.2'))
+    self.assertEquals('redirect', second_redirect_request.initiator['type'])
+    self.assertEquals(
+        first_redirect_request.request_id,
+        second_redirect_request.initiator[Request.INITIATING_REQUEST])
+    request = self.request_track._requests_in_flight.values()[0][0]
+    self.assertEquals('redirect', request.initiator['type'])
+    self.assertEquals(
+        second_redirect_request.request_id,
+        request.initiator[Request.INITIATING_REQUEST])
+    self.assertEquals(0, self.request_track.inconsistent_initiators_count)
+
+  def testInconsistentInitiators(self):
+    self.request_track.Handle('Network.requestWillBeSent',
+                              RequestTrackTestCase._REQUEST_WILL_BE_SENT)
+    request = copy.deepcopy(RequestTrackTestCase._REDIRECT)
+    request['params']['initiator']['type'] = 'script'
+    self.request_track.Handle('Network.requestWillBeSent', request)
+    self.assertEquals(1, self.request_track.inconsistent_initiators_count)
 
   def testRejectDuplicates(self):
     msg = RequestTrackTestCase._REQUEST_WILL_BE_SENT
@@ -281,6 +321,7 @@
   def testCanDeserialize(self):
     self._ValidSequence(self.request_track)
     self.request_track.duplicates_count = 142
+    self.request_track.inconsistent_initiators_count = 123
     json_dict = self.request_track.ToJsonDict()
     request_track = RequestTrack.FromJsonDict(json_dict)
     self.assertEquals(self.request_track, request_track)
diff --git a/tools/battor_agent/BUILD.gn b/tools/battor_agent/BUILD.gn
index 2f6dd284..1a057f3 100644
--- a/tools/battor_agent/BUILD.gn
+++ b/tools/battor_agent/BUILD.gn
@@ -28,6 +28,8 @@
     "battor_connection_impl.cc",
     "battor_connection_impl.h",
     "battor_error.h",
+    "battor_finder.cc",
+    "battor_finder.h",
     "battor_sample_converter.cc",
     "battor_sample_converter.h",
   ]
diff --git a/tools/battor_agent/DEPS b/tools/battor_agent/DEPS
index 67358f38..b40de52 100644
--- a/tools/battor_agent/DEPS
+++ b/tools/battor_agent/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+device/serial",
+  "+mojo/public",
   "+net/base",
 ]
\ No newline at end of file
diff --git a/tools/battor_agent/battor_agent.gyp b/tools/battor_agent/battor_agent.gyp
index 051f66dc..c34c5cd 100644
--- a/tools/battor_agent/battor_agent.gyp
+++ b/tools/battor_agent/battor_agent.gyp
@@ -35,6 +35,8 @@
         'battor_connection_impl.cc',
         'battor_connection_impl.h',
         'battor_error.h',
+        'battor_finder.cc',
+        'battor_finder.h',
         'battor_sample_converter.cc',
         'battor_sample_converter.h',
       ],
diff --git a/tools/battor_agent/battor_agent_bin.cc b/tools/battor_agent/battor_agent_bin.cc
index 360146d..93036e2fcb 100644
--- a/tools/battor_agent/battor_agent_bin.cc
+++ b/tools/battor_agent/battor_agent_bin.cc
@@ -19,6 +19,7 @@
 #include "base/threading/thread.h"
 #include "tools/battor_agent/battor_agent.h"
 #include "tools/battor_agent/battor_error.h"
+#include "tools/battor_agent/battor_finder.h"
 
 using std::cout;
 using std::endl;
@@ -47,12 +48,11 @@
   cout << battor::BattOrAgent::SupportsExplicitClockSync() << endl;
 }
 
-// Retrieves argument argnum from the argument list, printing the usage
-// guidelines and exiting with an error code if the argument doesn't exist.
+// Retrieves argument argnum from the argument list, or an empty string if the
+// argument doesn't exist.
 std::string GetArg(int argnum, int argc, char* argv[]) {
   if (argnum >= argc) {
-    PrintUsage();
-    exit(1);
+    return std::string();
   }
 
   return argv[argnum];
@@ -89,6 +89,10 @@
   // Runs the BattOr binary and returns the exit code.
   int Run(int argc, char* argv[]) {
     std::string cmd = GetArg(1, argc, argv);
+    if (cmd.empty()) {
+      PrintUsage();
+      exit(1);
+    }
 
     // SupportsExplicitClockSync doesn't need to use the serial connection, so
     // handle it separately.
@@ -98,6 +102,18 @@
     }
 
     std::string path = GetArg(2, argc, argv);
+    // If no path is specified, see if we can find a BattOr and use that.
+    if (path.empty())
+      path = BattOrFinder::FindBattOr();
+
+    // If we don't have any BattOr to use, exit.
+    if (path.empty()) {
+      cout << "Unable to find a BattOr, and no explicit BattOr path was "
+              "specified."
+           << endl;
+      exit(1);
+    }
+
     SetUp(path);
 
     if (cmd == "StartTracing") {
diff --git a/tools/battor_agent/battor_agent_unittest.cc b/tools/battor_agent/battor_agent_unittest.cc
index ad86f487..d1b43e9 100644
--- a/tools/battor_agent/battor_agent_unittest.cc
+++ b/tools/battor_agent/battor_agent_unittest.cc
@@ -472,9 +472,8 @@
 
   // Send an empty last frame to indicate that we're done.
   BattOrFrameHeader frame_header3{0, 0 * sizeof(RawBattOrSample)};
-  RawBattOrSample frame3[] = {};
   GetAgent()->OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
-                            CreateFrame(frame_header3, frame3, 0));
+                            CreateFrame(frame_header3, nullptr, 0));
 
   EXPECT_TRUE(IsCommandComplete());
   EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
@@ -523,9 +522,8 @@
   GetTaskRunner()->RunUntilIdle();
 
   BattOrFrameHeader frame_header{0, 0};
-  RawBattOrSample frame[] = {};
   GetAgent()->OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
-                            CreateFrame(frame_header, frame, 0));
+                            CreateFrame(frame_header, nullptr, 0));
   GetTaskRunner()->RunUntilIdle();
 
   EXPECT_TRUE(IsCommandComplete());
@@ -577,9 +575,8 @@
   GetTaskRunner()->RunUntilIdle();
 
   BattOrFrameHeader frame_header{0, 0};
-  RawBattOrSample frame[] = {};
   GetAgent()->OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
-                            CreateFrame(frame_header, frame, 0));
+                            CreateFrame(frame_header, nullptr, 0));
   GetTaskRunner()->RunUntilIdle();
 
   EXPECT_TRUE(IsCommandComplete());
@@ -607,9 +604,8 @@
   GetTaskRunner()->RunUntilIdle();
 
   BattOrFrameHeader frame_header{0, 0};
-  RawBattOrSample frame[] = {};
   GetAgent()->OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
-                            CreateFrame(frame_header, frame, 0));
+                            CreateFrame(frame_header, nullptr, 0));
   GetTaskRunner()->RunUntilIdle();
 
   EXPECT_TRUE(IsCommandComplete());
@@ -651,9 +647,8 @@
   }
 
   BattOrFrameHeader frame_header2{0, 0};
-  RawBattOrSample frame2[] = {};
   GetAgent()->OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
-                            CreateFrame(frame_header2, frame2, 0));
+                            CreateFrame(frame_header2, nullptr, 0));
   GetTaskRunner()->RunUntilIdle();
 
   EXPECT_TRUE(IsCommandComplete());
diff --git a/tools/battor_agent/battor_finder.cc b/tools/battor_agent/battor_finder.cc
new file mode 100644
index 0000000..b0e32db
--- /dev/null
+++ b/tools/battor_agent/battor_finder.cc
@@ -0,0 +1,39 @@
+// 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.
+
+#include "tools/battor_agent/battor_finder.h"
+
+#include "device/serial/serial.mojom.h"
+#include "device/serial/serial_device_enumerator.h"
+#include "mojo/public/cpp/bindings/array.h"
+
+namespace battor {
+
+namespace {
+
+// The USB display name prefix that all BattOrs have.
+const char kBattOrDisplayNamePrefix[] = "BattOr";
+
+}  // namespace
+
+// Returns the path of the first BattOr that we find.
+std::string BattOrFinder::FindBattOr() {
+  scoped_ptr<device::SerialDeviceEnumerator> serial_device_enumerator =
+      device::SerialDeviceEnumerator::Create();
+
+  mojo::Array<device::serial::DeviceInfoPtr> devices =
+      serial_device_enumerator->GetDevices();
+
+  for (size_t i = 0; i < devices.size(); i++) {
+    std::string display_name = devices[i]->display_name.get();
+
+    if (display_name.find(kBattOrDisplayNamePrefix) != std::string::npos) {
+      return devices[i]->path;
+    }
+  }
+
+  return std::string();
+}
+
+}  // namespace battor
diff --git a/tools/battor_agent/battor_finder.h b/tools/battor_agent/battor_finder.h
new file mode 100644
index 0000000..fe6fa79
--- /dev/null
+++ b/tools/battor_agent/battor_finder.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef TOOLS_BATTOR_AGENT_BATTOR_FINDER_H_
+#define TOOLS_BATTOR_AGENT_BATTOR_FINDER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace battor {
+
+class BattOrFinder {
+ public:
+  static std::string FindBattOr();
+
+  DISALLOW_COPY_AND_ASSIGN(BattOrFinder);
+};
+
+}  // namespace battor
+
+#endif  // TOOLS_BATTOR_AGENT_BATTOR_FINDER_H_
diff --git a/tools/battor_agent/battor_sample_converter_unittest.cc b/tools/battor_agent/battor_sample_converter_unittest.cc
index a20f878..f0b9935 100644
--- a/tools/battor_agent/battor_sample_converter_unittest.cc
+++ b/tools/battor_agent/battor_sample_converter_unittest.cc
@@ -15,12 +15,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleSimple) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 1;
-  eeprom.r2 = 1;
-  eeprom.r3 = 1;
-  eeprom.low_gain = 1;
-  eeprom.low_gain_correction_offset = 0;
-  eeprom.low_gain_correction_factor = 1;
+  eeprom.r1 = 1.0f;
+  eeprom.r2 = 1.0f;
+  eeprom.r3 = 1.0f;
+  eeprom.low_gain = 1.0f;
+  eeprom.low_gain_correction_offset = 0.0f;
+  eeprom.low_gain_correction_factor = 1.0f;
   eeprom.sd_sample_rate = 1000;
 
   // Create a calibration frame with a baseline voltage and current of zero.
@@ -39,12 +39,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleNonZeroBaseline) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 1;
-  eeprom.r2 = 1;
-  eeprom.r3 = 1;
-  eeprom.low_gain = 1;
-  eeprom.low_gain_correction_offset = 0;
-  eeprom.low_gain_correction_factor = 1;
+  eeprom.r1 = 1.0f;
+  eeprom.r2 = 1.0f;
+  eeprom.r3 = 1.0f;
+  eeprom.low_gain = 1.0f;
+  eeprom.low_gain_correction_offset = 0.0f;
+  eeprom.low_gain_correction_factor = 1.0f;
   eeprom.sd_sample_rate = 1000;
 
   // Create a calibration frame with a baseline voltage and current of zero.
@@ -63,12 +63,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleNonZeroMultiSampleBaseline) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 1;
-  eeprom.r2 = 1;
-  eeprom.r3 = 1;
-  eeprom.low_gain = 1;
-  eeprom.low_gain_correction_offset = 0;
-  eeprom.low_gain_correction_factor = 1;
+  eeprom.r1 = 1.0f;
+  eeprom.r2 = 1.0f;
+  eeprom.r3 = 1.0f;
+  eeprom.low_gain = 1.0f;
+  eeprom.low_gain_correction_offset = 0.0f;
+  eeprom.low_gain_correction_factor = 1.0f;
   eeprom.sd_sample_rate = 1000;
 
   // Create a calibration frame with a baseline voltage and current of zero.
@@ -88,12 +88,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleRealValues) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 10;
-  eeprom.r2 = 14;
-  eeprom.r3 = 17;
+  eeprom.r1 = 10.0f;
+  eeprom.r2 = 14.0f;
+  eeprom.r3 = 17.0f;
   eeprom.low_gain = 1.5;
-  eeprom.low_gain_correction_offset = 0.03;
-  eeprom.low_gain_correction_factor = 4;
+  eeprom.low_gain_correction_offset = 0.03f;
+  eeprom.low_gain_correction_factor = 4.0f;
   eeprom.sd_sample_rate = 1000;
 
   // Create a calibration frame with a baseline voltage and current of zero.
@@ -113,12 +113,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleRealNegativeValues) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 10;
-  eeprom.r2 = 14;
-  eeprom.r3 = 17;
+  eeprom.r1 = 10.0f;
+  eeprom.r2 = 14.0f;
+  eeprom.r3 = 17.0f;
   eeprom.low_gain = 1.5;
-  eeprom.low_gain_correction_offset = 0.03;
-  eeprom.low_gain_correction_factor = 4;
+  eeprom.low_gain_correction_offset = 0.03f;
+  eeprom.low_gain_correction_factor = 4.0f;
   eeprom.sd_sample_rate = 1000;
 
   // Create a calibration frame with a baseline voltage and current of zero.
@@ -137,12 +137,12 @@
 
 TEST(BattOrSampleConverterTest, ToSampleMultipleSamples) {
   BattOrEEPROM eeprom;
-  eeprom.r1 = 1;
-  eeprom.r2 = 1;
-  eeprom.r3 = 1;
-  eeprom.low_gain = 1;
-  eeprom.low_gain_correction_offset = 0;
-  eeprom.low_gain_correction_factor = 1;
+  eeprom.r1 = 1.0f;
+  eeprom.r2 = 1.0f;
+  eeprom.r3 = 1.0f;
+  eeprom.low_gain = 1.0f;
+  eeprom.low_gain_correction_offset = 0.0f;
+  eeprom.low_gain_correction_factor = 1.0f;
   eeprom.sd_sample_rate = 50;
 
   std::vector<RawBattOrSample> calibration_frame;
diff --git a/tools/cr/cr/commands/run.py b/tools/cr/cr/commands/run.py
index 1b97b75..15d7db06 100644
--- a/tools/cr/cr/commands/run.py
+++ b/tools/cr/cr/commands/run.py
@@ -25,7 +25,7 @@
     cr.Builder.AddArguments(self, parser)
     cr.Installer.AddArguments(self, parser)
     cr.Runner.AddArguments(self, parser)
-    cr.Target.AddArguments(self, parser, allow_multiple=True)
+    cr.Target.AddArguments(self, parser, allow_multiple=False)
     self.ConsumeArgs(parser, 'the binary')
     return parser
 
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index 97f12f2..85fba66 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -1327,7 +1327,8 @@
 ## **forward_variables_from**: Copies variables from a different scope.
 
 ```
-  forward_variables_from(from_scope, variable_list_or_star)
+  forward_variables_from(from_scope, variable_list_or_star,
+                         variable_to_not_forward_list = [])
 
   Copies the given variables from the given scope to the local scope
   if they exist. This is normally used in the context of templates to
@@ -1354,6 +1355,10 @@
   is never applied by this function. It's assumed than any desired
   filtering was already done when sources was set on the from_scope.
 
+  If variables_to_not_forward_list is non-empty, then it must contains
+  a list of variable names that will not be forwarded. This is mostly
+  useful when variable_list_or_star has a value of "*".
+
 ```
 
 ### **Examples**
@@ -1383,7 +1388,19 @@
     target(my_wrapper_target_type, target_name) {
       forward_variables_from(invoker, "*")
     }
- }
+  }
+
+  # A template that wraps another. It adds behavior based on one
+  # variable, and forwards all others to the nested target.
+  template("my_ios_test_app") {
+    ios_test_app(target_name) {
+      forward_variables_from(invoker, "*", ["test_bundle_name"])
+      if (!defined(extra_substitutions)) {
+        extra_substitutions = []
+      }
+      extra_substitutions += [ "BUNDLE_ID_TEST_NAME=$test_bundle_name" ]
+    }
+  }
 
 
 ```
diff --git a/tools/gn/function_forward_variables_from.cc b/tools/gn/function_forward_variables_from.cc
index 9c97a51c..ee14128 100644
--- a/tools/gn/function_forward_variables_from.cc
+++ b/tools/gn/function_forward_variables_from.cc
@@ -14,6 +14,7 @@
 void ForwardAllValues(const FunctionCallNode* function,
                       Scope* source,
                       Scope* dest,
+                      const std::set<std::string>& exclusion_set,
                       Err* err) {
   Scope::MergeOptions options;
   // This function needs to clobber existing for it to be useful. It will be
@@ -23,6 +24,7 @@
   options.clobber_existing = true;
   options.skip_private_vars = true;
   options.mark_dest_used = false;
+  options.excluded_values = exclusion_set;
   source->NonRecursiveMergeTo(dest, options, function,
                               "source scope", err);
   source->MarkAllUsed();
@@ -31,10 +33,13 @@
 void ForwardValuesFromList(Scope* source,
                            Scope* dest,
                            const std::vector<Value>& list,
+                           const std::set<std::string>& exclusion_set,
                            Err* err) {
   for (const Value& cur : list) {
     if (!cur.VerifyTypeIs(Value::STRING, err))
       return;
+    if (exclusion_set.find(cur.string_value()) != exclusion_set.end())
+      continue;
     const Value* value = source->GetValue(cur.string_value(), true);
     if (value) {
       // Use the storage key for the original value rather than the string in
@@ -66,7 +71,8 @@
 const char kForwardVariablesFrom_Help[] =
     "forward_variables_from: Copies variables from a different scope.\n"
     "\n"
-    "  forward_variables_from(from_scope, variable_list_or_star)\n"
+    "  forward_variables_from(from_scope, variable_list_or_star,\n"
+    "                         variable_to_not_forward_list = [])\n"
     "\n"
     "  Copies the given variables from the given scope to the local scope\n"
     "  if they exist. This is normally used in the context of templates to\n"
@@ -94,6 +100,10 @@
     "  is never applied by this function. It's assumed than any desired\n"
     "  filtering was already done when sources was set on the from_scope.\n"
     "\n"
+    "  If variables_to_not_forward_list is non-empty, then it must contains\n"
+    "  a list of variable names that will not be forwarded. This is mostly\n"
+    "  useful when variable_list_or_star has a value of \"*\".\n"
+    "\n"
     "Examples\n"
     "\n"
     "  # This is a common action template. It would invoke a script with\n"
@@ -121,7 +131,20 @@
     "    target(my_wrapper_target_type, target_name) {\n"
     "      forward_variables_from(invoker, \"*\")\n"
     "    }\n"
-    " }\n";
+    "  }\n"
+    "\n"
+    "  # A template that wraps another. It adds behavior based on one \n"
+    "  # variable, and forwards all others to the nested target.\n"
+    "  template(\"my_ios_test_app\") {\n"
+    "    ios_test_app(target_name) {\n"
+    "      forward_variables_from(invoker, \"*\", [\"test_bundle_name\"])\n"
+    "      if (!defined(extra_substitutions)) {\n"
+    "        extra_substitutions = []\n"
+    "      }\n"
+    "      extra_substitutions += [ \"BUNDLE_ID_TEST_NAME=$test_bundle_name\" "
+                                                                          "]\n"
+    "    }\n"
+    "  }\n";
 
 // This function takes a ListNode rather than a resolved vector of values
 // both avoid copying the potentially-large source scope, and so the variables
@@ -131,9 +154,9 @@
                               const ListNode* args_list,
                               Err* err) {
   const std::vector<const ParseNode*>& args_vector = args_list->contents();
-  if (args_vector.size() != 2) {
+  if (args_vector.size() != 2 && args_vector.size() != 3) {
     *err = Err(function, "Wrong number of arguments.",
-               "Expecting exactly two.");
+               "Expecting two or three arguments.");
     return Value();
   }
 
@@ -157,18 +180,40 @@
     return Value();
   Scope* source = value->scope_value();
 
+  // Extract the exclusion list if defined.
+  std::set<std::string> exclusion_set;
+  if (args_vector.size() == 3) {
+    Value exclusion_value = args_vector[2]->Execute(scope, err);
+    if (err->has_error())
+      return Value();
+
+    if (exclusion_value.type() != Value::LIST) {
+      *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
+                 "Expecting a list of strings.");
+      return Value();
+    }
+
+    for (const Value& cur : exclusion_value.list_value()) {
+      if (!cur.VerifyTypeIs(Value::STRING, err))
+        return Value();
+
+      exclusion_set.insert(cur.string_value());
+    }
+  }
+
   // Extract the list. If all_values is not set, the what_value will be a list.
   Value what_value = args_vector[1]->Execute(scope, err);
   if (err->has_error())
     return Value();
   if (what_value.type() == Value::STRING) {
     if (what_value.string_value() == "*") {
-      ForwardAllValues(function, source, scope, err);
+      ForwardAllValues(function, source, scope, exclusion_set, err);
       return Value();
     }
   } else {
     if (what_value.type() == Value::LIST) {
-      ForwardValuesFromList(source, scope, what_value.list_value(), err);
+      ForwardValuesFromList(source, scope, what_value.list_value(),
+                            exclusion_set, err);
       return Value();
     }
   }
diff --git a/tools/gn/function_forward_variables_from_unittest.cc b/tools/gn/function_forward_variables_from_unittest.cc
index c3dd715..e5137a4 100644
--- a/tools/gn/function_forward_variables_from_unittest.cc
+++ b/tools/gn/function_forward_variables_from_unittest.cc
@@ -32,6 +32,34 @@
   setup.print_output().clear();
 }
 
+TEST(FunctionForwardVariablesFrom, ListWithExclusion) {
+  Scheduler scheduler;
+  TestWithScope setup;
+
+  // Defines a template and copy the two x and y, and z values out.
+  TestParseInput input(
+    "template(\"a\") {\n"
+    "  forward_variables_from(invoker, [\"x\", \"y\", \"z\"], [\"z\"])\n"
+    "  assert(!defined(z))\n"  // "z" should still be undefined.
+    "  print(\"$target_name, $x, $y\")\n"
+    "}\n"
+    "a(\"target\") {\n"
+    "  x = 1\n"
+    "  y = 2\n"
+    "  z = 3\n"
+    "  print(\"$z\")\n"
+    "}\n");
+
+  ASSERT_FALSE(input.has_error());
+
+  Err err;
+  input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+
+  EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
+  setup.print_output().clear();
+}
+
 TEST(FunctionForwardVariablesFrom, ErrorCases) {
   Scheduler scheduler;
   TestWithScope setup;
@@ -65,19 +93,61 @@
   EXPECT_TRUE(err.has_error());
   EXPECT_EQ("Not a valid list of variables to copy.", err.message());
 
-  // Programmatic values should error.
-  TestParseInput prog(
+  // Type check the exclusion list.
+  TestParseInput invalid_exclusion_list(
     "template(\"c\") {\n"
-    "  forward_variables_from(invoker, [\"root_out_dir\"])\n"
+    "  forward_variables_from(invoker, \"*\", 42)\n"
     "  print(\"$target_name\")\n"
     "}\n"
     "c(\"target\") {\n"
     "}\n");
+  ASSERT_FALSE(invalid_exclusion_list.has_error());
+  err = Err();
+  invalid_exclusion_list.parsed()->Execute(setup.scope(), &err);
+  EXPECT_TRUE(err.has_error());
+  EXPECT_EQ("Not a valid list of variables to exclude.", err.message());
+
+  // Programmatic values should error.
+  TestParseInput prog(
+    "template(\"d\") {\n"
+    "  forward_variables_from(invoker, [\"root_out_dir\"])\n"
+    "  print(\"$target_name\")\n"
+    "}\n"
+    "d(\"target\") {\n"
+    "}\n");
   ASSERT_FALSE(prog.has_error());
   err = Err();
   prog.parsed()->Execute(setup.scope(), &err);
   EXPECT_TRUE(err.has_error());
   EXPECT_EQ("This value can't be forwarded.", err.message());
+
+  // Not enough arguments.
+  TestParseInput not_enough_arguments(
+    "template(\"e\") {\n"
+    "  forward_variables_from(invoker)\n"
+    "  print(\"$target_name\")\n"
+    "}\n"
+    "e(\"target\") {\n"
+    "}\n");
+  ASSERT_FALSE(not_enough_arguments.has_error());
+  err = Err();
+  not_enough_arguments.parsed()->Execute(setup.scope(), &err);
+  EXPECT_TRUE(err.has_error());
+  EXPECT_EQ("Wrong number of arguments.", err.message());
+
+  // Too many arguments.
+  TestParseInput too_many_arguments(
+    "template(\"f\") {\n"
+    "  forward_variables_from(invoker, \"*\", [], [])\n"
+    "  print(\"$target_name\")\n"
+    "}\n"
+    "f(\"target\") {\n"
+    "}\n");
+  ASSERT_FALSE(too_many_arguments.has_error());
+  err = Err();
+  too_many_arguments.parsed()->Execute(setup.scope(), &err);
+  EXPECT_TRUE(err.has_error());
+  EXPECT_EQ("Wrong number of arguments.", err.message());
 }
 
 TEST(FunctionForwardVariablesFrom, Star) {
@@ -106,3 +176,33 @@
   EXPECT_EQ("target, 1, 2\n", setup.print_output());
   setup.print_output().clear();
 }
+
+
+TEST(FunctionForwardVariablesFrom, StarWithExclusion) {
+  Scheduler scheduler;
+  TestWithScope setup;
+
+  // Defines a template and copy all values except z value. The "*" behavior
+  // should clobber existing variables with the same name.
+  TestParseInput input(
+    "template(\"a\") {\n"
+    "  x = 1000000\n"  // Should be clobbered.
+    "  forward_variables_from(invoker, \"*\", [\"z\"])\n"
+    "  print(\"$target_name, $x, $y\")\n"
+    "}\n"
+    "a(\"target\") {\n"
+    "  x = 1\n"
+    "  y = 2\n"
+    "  z = 3\n"
+    "  print(\"$z\")\n"
+    "}\n");
+
+  ASSERT_FALSE(input.has_error());
+
+  Err err;
+  input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error()) << err.message();
+
+  EXPECT_EQ("3\ntarget, 1, 2\n", setup.print_output());
+  setup.print_output().clear();
+}
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
index 2bf9dd4..dc22367 100644
--- a/tools/gn/scope.cc
+++ b/tools/gn/scope.cc
@@ -25,6 +25,15 @@
 
 }  // namespace
 
+// Defaults to all false, which are the things least likely to cause errors.
+Scope::MergeOptions::MergeOptions()
+    : clobber_existing(false),
+      skip_private_vars(false),
+      mark_dest_used(false) {
+}
+
+Scope::MergeOptions::~MergeOptions() {
+}
 
 Scope::ProgrammaticProvider::~ProgrammaticProvider() {
   scope_->RemoveProvider(this);
@@ -249,17 +258,23 @@
                                 Err* err) const {
   // Values.
   for (const auto& pair : values_) {
-    if (options.skip_private_vars && IsPrivateVar(pair.first))
+    const base::StringPiece& current_name = pair.first;
+    if (options.skip_private_vars && IsPrivateVar(current_name))
       continue;  // Skip this private var.
+    if (!options.excluded_values.empty() &&
+        options.excluded_values.find(current_name.as_string()) !=
+            options.excluded_values.end()) {
+      continue;  // Skip this excluded value.
+    }
 
     const Value& new_value = pair.second.value;
     if (!options.clobber_existing) {
-      const Value* existing_value = dest->GetValue(pair.first);
+      const Value* existing_value = dest->GetValue(current_name);
       if (existing_value && new_value != *existing_value) {
         // Value present in both the source and the dest.
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Value collision.",
-            "This " + desc_string + " contains \"" + pair.first.as_string() +
+            "This " + desc_string + " contains \"" + current_name.as_string() +
             "\"");
         err->AppendSubErr(Err(pair.second.value, "defined here.",
             "Which would clobber the one in your current scope"));
@@ -269,23 +284,30 @@
         return false;
       }
     }
-    dest->values_[pair.first] = pair.second;
+    dest->values_[current_name] = pair.second;
 
     if (options.mark_dest_used)
-      dest->MarkUsed(pair.first);
+      dest->MarkUsed(current_name);
   }
 
   // Target defaults are owning pointers.
   for (const auto& pair : target_defaults_) {
+    const std::string& current_name = pair.first;
+    if (!options.excluded_values.empty() &&
+        options.excluded_values.find(current_name) !=
+            options.excluded_values.end()) {
+      continue;  // Skip the excluded value.
+    }
+
     if (!options.clobber_existing) {
-      if (dest->GetTargetDefaults(pair.first)) {
+      if (dest->GetTargetDefaults(current_name)) {
         // TODO(brettw) it would be nice to know the origin of a
         // set_target_defaults so we can give locations for the colliding target
         // defaults.
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Target defaults collision.",
             "This " + desc_string + " contains target defaults for\n"
-            "\"" + pair.first + "\" which would clobber one for the\n"
+            "\"" + current_name + "\" which would clobber one for the\n"
             "same target type in your current scope. It's unfortunate that I'm "
             "too stupid\nto tell you the location of where the target defaults "
             "were set. Usually\nthis happens in the BUILDCONFIG.gn file.");
@@ -294,7 +316,7 @@
     }
 
     // Be careful to delete any pointer we're about to clobber.
-    Scope** dest_scope = &dest->target_defaults_[pair.first];
+    Scope** dest_scope = &dest->target_defaults_[current_name];
     if (*dest_scope)
       delete *dest_scope;
     *dest_scope = new Scope(settings_);
@@ -320,11 +342,17 @@
 
   // Templates.
   for (const auto& pair : templates_) {
-    if (options.skip_private_vars && IsPrivateVar(pair.first))
+    const std::string& current_name = pair.first;
+    if (options.skip_private_vars && IsPrivateVar(current_name))
       continue;  // Skip this private template.
+    if (!options.excluded_values.empty() &&
+        options.excluded_values.find(current_name) !=
+            options.excluded_values.end()) {
+      continue;  // Skip the excluded value.
+    }
 
     if (!options.clobber_existing) {
-      const Template* existing_template = dest->GetTemplate(pair.first);
+      const Template* existing_template = dest->GetTemplate(current_name);
       // Since templates are refcounted, we can check if it's the same one by
       // comparing pointers.
       if (existing_template && pair.second.get() != existing_template) {
@@ -333,7 +361,7 @@
         std::string desc_string(desc_for_err);
         *err = Err(node_for_err, "Template collision.",
             "This " + desc_string + " contains a template \"" +
-            pair.first + "\"");
+            current_name + "\"");
         err->AppendSubErr(Err(pair.second->GetDefinitionRange(),
             "defined here.",
             "Which would clobber the one in your current scope"));
@@ -346,7 +374,7 @@
     }
 
     // Be careful to delete any pointer we're about to clobber.
-    dest->templates_[pair.first] = pair.second;
+    dest->templates_[current_name] = pair.second;
   }
 
   return true;
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
index 7b32941..2554366a 100644
--- a/tools/gn/scope.h
+++ b/tools/gn/scope.h
@@ -65,12 +65,8 @@
 
   // Options for configuring scope merges.
   struct MergeOptions {
-    // Defaults to all false, which are the things least likely to cause errors.
-    MergeOptions()
-        : clobber_existing(false),
-          skip_private_vars(false),
-          mark_dest_used(false) {
-    }
+    MergeOptions();
+    ~MergeOptions();
 
     // When set, all existing avlues in the destination scope will be
     // overwritten.
@@ -92,6 +88,9 @@
     // import, for example, or files that don't need a variable from the .gni
     // file will throw an error.
     bool mark_dest_used;
+
+    // When set, those variables are not merged.
+    std::set<std::string> excluded_values;
   };
 
   // Creates an empty toplevel scope.
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 459b87b..79814e6 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -31,6 +31,7 @@
 #include "tools/gn/tokenizer.h"
 #include "tools/gn/trace.h"
 #include "tools/gn/value.h"
+#include "tools/gn/value_extractors.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -655,21 +656,13 @@
   const Value* check_targets_value =
       dotfile_scope_.GetValue("check_targets", true);
   if (check_targets_value) {
-    // Fill the list of targets to check.
-    if (!check_targets_value->VerifyTypeIs(Value::LIST, &err)) {
+    check_patterns_.reset(new std::vector<LabelPattern>);
+    ExtractListOfLabelPatterns(*check_targets_value, current_dir,
+                               check_patterns_.get(), &err);
+    if (err.has_error()) {
       err.PrintToStdout();
       return false;
     }
-
-    check_patterns_.reset(new std::vector<LabelPattern>);
-    for (const auto& item : check_targets_value->list_value()) {
-      check_patterns_->push_back(
-          LabelPattern::GetPattern(current_dir, item, &err));
-      if (err.has_error()) {
-        err.PrintToStdout();
-        return false;
-      }
-    }
   }
 
   // Fill exec_script_whitelist.
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 905ee4f..7dd1921 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -129,6 +129,61 @@
   return false;
 }
 
+// check_this indicates if the given target should be matched against the
+// patterns. It should be set to false for the first call since assert_no_deps
+// shouldn't match the target itself.
+//
+// visited should point to an empty set, this will be used to prevent
+// multiple visits.
+//
+// *failure_path_str will be filled with a string describing the path of the
+// dependency failure, and failure_pattern will indicate the pattern in
+// assert_no that matched the target.
+//
+// Returns true if everything is OK. failure_path_str and failure_pattern_index
+// will be unchanged in this case.
+bool RecursiveCheckAssertNoDeps(const Target* target,
+                                bool check_this,
+                                const std::vector<LabelPattern>& assert_no,
+                                std::set<const Target*>* visited,
+                                std::string* failure_path_str,
+                                const LabelPattern** failure_pattern) {
+  static const char kIndentPath[] = "  ";
+
+  if (visited->find(target) != visited->end())
+    return true;  // Already checked this target.
+  visited->insert(target);
+
+  if (check_this) {
+    // Check this target against the given list of patterns.
+    for (const LabelPattern& pattern : assert_no) {
+      if (pattern.Matches(target->label())) {
+        // Found a match.
+        *failure_pattern = &pattern;
+        *failure_path_str =
+            kIndentPath + target->label().GetUserVisibleName(false);
+        return false;
+      }
+    }
+  }
+
+  // Recursively check dependencies.
+  for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
+    if (pair.ptr->output_type() == Target::EXECUTABLE)
+      continue;
+    if (!RecursiveCheckAssertNoDeps(pair.ptr, true, assert_no, visited,
+                                    failure_path_str, failure_pattern)) {
+      // To reconstruct the path, prepend the current target to the error.
+      std::string prepend_path =
+          kIndentPath + target->label().GetUserVisibleName(false) + " ->\n";
+      failure_path_str->insert(0, prepend_path);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace
 
 Target::Target(const Settings* settings, const Label& label)
@@ -234,6 +289,8 @@
       return false;
     if (!CheckNoNestedStaticLibs(err))
       return false;
+    if (!CheckAssertNoDeps(err))
+      return false;
     CheckSourcesGenerated();
   }
 
@@ -604,6 +661,27 @@
   return true;
 }
 
+bool Target::CheckAssertNoDeps(Err* err) const {
+  if (assert_no_deps_.empty())
+    return true;
+
+  std::set<const Target*> visited;
+  std::string failure_path_str;
+  const LabelPattern* failure_pattern = nullptr;
+
+  if (!RecursiveCheckAssertNoDeps(this, false, assert_no_deps_, &visited,
+                                  &failure_path_str, &failure_pattern)) {
+    *err = Err(defined_from(), "assert_no_deps failed.",
+        label().GetUserVisibleName(false) +
+        " has an assert_no_deps entry:\n  " +
+        failure_pattern->Describe() +
+        "\nwhich fails for the dependency path:\n" +
+        failure_path_str);
+    return false;
+  }
+  return true;
+}
+
 void Target::CheckSourcesGenerated() const {
   // Checks that any inputs or sources to this target that are in the build
   // directory are generated by a target that this one transitively depends on
diff --git a/tools/gn/target.h b/tools/gn/target.h
index cbdab357..18e5fb8 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -16,6 +16,7 @@
 #include "tools/gn/config_values.h"
 #include "tools/gn/inherited_libraries.h"
 #include "tools/gn/item.h"
+#include "tools/gn/label_pattern.h"
 #include "tools/gn/label_ptr.h"
 #include "tools/gn/lib_file.h"
 #include "tools/gn/ordered_set.h"
@@ -209,6 +210,13 @@
     return recursive_hard_deps_;
   }
 
+  std::vector<LabelPattern>& assert_no_deps() {
+    return assert_no_deps_;
+  }
+  const std::vector<LabelPattern>& assert_no_deps() const {
+    return assert_no_deps_;
+  }
+
   // The toolchain is only known once this target is resolved (all if its
   // dependencies are known). They will be null until then. Generally, this can
   // only be used during target writing.
@@ -284,6 +292,7 @@
   bool CheckVisibility(Err* err) const;
   bool CheckTestonly(Err* err) const;
   bool CheckNoNestedStaticLibs(Err* err) const;
+  bool CheckAssertNoDeps(Err* err) const;
   void CheckSourcesGenerated() const;
   void CheckSourceGenerated(const SourceFile& source) const;
 
@@ -324,6 +333,8 @@
   // target is marked resolved. This will not include the current target.
   std::set<const Target*> recursive_hard_deps_;
 
+  std::vector<LabelPattern> assert_no_deps_;
+
   // Used for all binary targets. The precompiled header values in this struct
   // will be resolved to the ones to use for this target, if precompiled
   // headers are used.
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index 3734ea9c..b1ed1ea3 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -50,6 +50,9 @@
   if (!FillTestonly())
     return;
 
+  if (!FillAssertNoDeps())
+    return;
+
   if (!Visibility::FillItemVisibility(target_, scope_, err_))
     return;
 
@@ -266,6 +269,15 @@
   return true;
 }
 
+bool TargetGenerator::FillAssertNoDeps() {
+  const Value* value = scope_->GetValue(variables::kAssertNoDeps, true);
+  if (value) {
+    return ExtractListOfLabelPatterns(*value, scope_->GetSourceDir(),
+                                      &target_->assert_no_deps(), err_);
+  }
+  return true;
+}
+
 bool TargetGenerator::FillOutputs(bool allow_substitutions) {
   const Value* value = scope_->GetValue(variables::kOutputs, true);
   if (!value)
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
index 1fd055b1..4fa51f6 100644
--- a/tools/gn/target_generator.h
+++ b/tools/gn/target_generator.h
@@ -70,6 +70,7 @@
   bool FillData();
   bool FillDependencies();  // Includes data dependencies.
   bool FillTestonly();
+  bool FillAssertNoDeps();
 
   // Reads configs/deps from the given var name, and uses the given setting on
   // the target to save them.
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index c59f44c..c99ebe8 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -668,3 +668,58 @@
       "  source: //pcs2.cc",
       err.help_text());
 }
+
+TEST(Target, AssertNoDeps) {
+  TestWithScope setup;
+  Err err;
+
+  // A target.
+  TestTarget a(setup, "//a", Target::SHARED_LIBRARY);
+  ASSERT_TRUE(a.OnResolved(&err));
+
+  // B depends on A and has an assert_no_deps for a random dir.
+  TestTarget b(setup, "//b", Target::SHARED_LIBRARY);
+  b.private_deps().push_back(LabelTargetPair(&a));
+  b.assert_no_deps().push_back(LabelPattern(
+      LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//disallowed/"),
+      std::string(), Label()));
+  ASSERT_TRUE(b.OnResolved(&err));
+
+  LabelPattern disallow_a(LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//a/"),
+                          std::string(), Label());
+
+  // C depends on B and disallows depending on A. This should fail.
+  TestTarget c(setup, "//c", Target::EXECUTABLE);
+  c.private_deps().push_back(LabelTargetPair(&b));
+  c.assert_no_deps().push_back(disallow_a);
+  ASSERT_FALSE(c.OnResolved(&err));
+
+  // Validate the error message has the proper path.
+  EXPECT_EQ(
+      "//c:c has an assert_no_deps entry:\n"
+      "  //a/*\n"
+      "which fails for the dependency path:\n"
+      "  //c:c ->\n"
+      "  //b:b ->\n"
+      "  //a:a",
+      err.help_text());
+  err = Err();
+
+  // Add an intermediate executable with: exe -> b -> a
+  TestTarget exe(setup, "//exe", Target::EXECUTABLE);
+  exe.private_deps().push_back(LabelTargetPair(&b));
+  ASSERT_TRUE(exe.OnResolved(&err));
+
+  // D depends on the executable and disallows depending on A. Since there is
+  // an intermediate executable, this should be OK.
+  TestTarget d(setup, "//d", Target::EXECUTABLE);
+  d.private_deps().push_back(LabelTargetPair(&exe));
+  d.assert_no_deps().push_back(disallow_a);
+  ASSERT_TRUE(d.OnResolved(&err));
+
+  // A2 disallows depending on anything in its own directory, but the
+  // assertions should not match the target itself so this should be OK.
+  TestTarget a2(setup, "//a:a2", Target::EXECUTABLE);
+  a2.assert_no_deps().push_back(disallow_a);
+  ASSERT_TRUE(a2.OnResolved(&err));
+}
diff --git a/tools/gn/value_extractors.cc b/tools/gn/value_extractors.cc
index 23c5a272..ff009ce 100644
--- a/tools/gn/value_extractors.cc
+++ b/tools/gn/value_extractors.cc
@@ -144,6 +144,17 @@
   const Label& current_toolchain;
 };
 
+struct LabelPatternResolver {
+  LabelPatternResolver(const SourceDir& current_dir_in)
+      : current_dir(current_dir_in) {
+  }
+  bool operator()(const Value& v, LabelPattern* out, Err* err) const {
+    *out = LabelPattern::GetPattern(current_dir, v, err);
+    return !err->has_error();
+  }
+  const SourceDir& current_dir;
+};
+
 }  // namespace
 
 bool ExtractListOfStringValues(const Value& value,
@@ -236,3 +247,11 @@
   RelativeFileConverter converter(build_settings, current_dir);
   return converter(value, file, err);
 }
+
+bool ExtractListOfLabelPatterns(const Value& value,
+                                const SourceDir& current_dir,
+                                std::vector<LabelPattern>* patterns,
+                                Err* err) {
+  return ListValueExtractor(value, patterns, err,
+                            LabelPatternResolver(current_dir));
+}
diff --git a/tools/gn/value_extractors.h b/tools/gn/value_extractors.h
index 06d64ce..1e426502 100644
--- a/tools/gn/value_extractors.h
+++ b/tools/gn/value_extractors.h
@@ -15,6 +15,7 @@
 class BuildSettings;
 class Err;
 class Label;
+class LabelPattern;
 class SourceDir;
 class SourceFile;
 class Value;
@@ -80,4 +81,9 @@
                          SourceFile* file,
                          Err* err);
 
+bool ExtractListOfLabelPatterns(const Value& value,
+                                const SourceDir& current_dir,
+                                std::vector<LabelPattern>* patterns,
+                                Err* err);
+
 #endif  // TOOLS_GN_VALUE_EXTRACTORS_H_
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index a835b22..c88a182f 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -391,6 +391,49 @@
     "\n"
     "  See also \"gn help action\" and \"gn help action_foreach\".\n";
 
+const char kAssertNoDeps[] = "assert_no_deps";
+const char kAssertNoDeps_HelpShort[] =
+    "assert_no_deps: [label pattern list] Ensure no deps on these targets.";
+const char kAssertNoDeps_Help[] =
+    "assert_no_deps: Ensure no deps on these targets.\n"
+    "\n"
+    "  A list of label patterns.\n"
+    "\n"
+    "  This list is a list of patterns that must not match any of the\n"
+    "  transitive dependencies of the target. These include all public,\n"
+    "  private, and data dependencies, and cross shared library boundaries.\n"
+    "  This allows you to express that undesirable code isn't accidentally\n"
+    "  added to downstream dependencies in a way that might otherwise be\n"
+    "  difficult to notice.\n"
+    "\n"
+    "  Checking does not cross executable boundaries. If a target depends on\n"
+    "  an executable, it's assumed that the executable is a tool that is\n"
+    "  producing part of the build rather than something that is linked and\n"
+    "  distributed. This allows assert_no_deps to express what is distributed\n"
+    "  in the final target rather than depend on the internal build steps\n"
+    "  (which may include non-distributable code).\n"
+    "\n"
+    "  See \"gn help label_pattern\" for the format of the entries in the\n"
+    "  list. These patterns allow blacklisting individual targets or whole\n"
+    "  directory hierarchies.\n"
+    "\n"
+    "  Sometimes it is desirable to enforce that many targets have no\n"
+    "  dependencies on a target or set of targets. One efficient way to\n"
+    "  express this is to create a group with the assert_no_deps rule on\n"
+    "  it, and make that group depend on all targets you want to apply that\n"
+    "  assertion to.\n"
+    "\n"
+    "Example\n"
+    "\n"
+    "  executable(\"doom_melon\") {\n"
+    "    deps = [ \"//foo:bar\" ]\n"
+    "    ...\n"
+    "    assert_no_deps = [\n"
+    "      \"//evil/*\",  # Don't link any code from the evil directory.\n"
+    "      \"//foo:test_support\",  # This target is also disallowed.\n"
+    "    ]\n"
+    "  }\n";
+
 const char kCflags[] = "cflags";
 const char kCflags_HelpShort[] =
     "cflags: [string list] Flags passed to all C compiler variants.";
@@ -1400,6 +1443,7 @@
     INSERT_VARIABLE(AllowCircularIncludesFrom)
     INSERT_VARIABLE(Args)
     INSERT_VARIABLE(Asmflags)
+    INSERT_VARIABLE(AssertNoDeps)
     INSERT_VARIABLE(Cflags)
     INSERT_VARIABLE(CflagsC)
     INSERT_VARIABLE(CflagsCC)
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index d02d2c6f..ee68bd52 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -87,6 +87,10 @@
 extern const char kAsmflags_HelpShort[];
 extern const char* kAsmflags_Help;
 
+extern const char kAssertNoDeps[];
+extern const char kAssertNoDeps_HelpShort[];
+extern const char kAssertNoDeps_Help[];
+
 extern const char kCflags[];
 extern const char kCflags_HelpShort[];
 extern const char* kCflags_Help;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b5a898b6..63662ee 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -10362,6 +10362,9 @@
 </histogram>
 
 <histogram name="Enterprise.SystemLogPIILeak" enum="SystemLogPIIType">
+  <obsolete>
+    Deprecated and removed from code as of 01/2016.
+  </obsolete>
   <owner>pbond@chromium.org</owner>
   <summary>
     Events for counting sensitive data occurrences in system logs to upload.
@@ -79678,6 +79681,9 @@
 </enum>
 
 <enum name="SystemLogPIIType" type="int">
+  <obsolete>
+    Deprecated and removed from code as of 01/2016.
+  </obsolete>
   <int value="0" label="Email address"/>
   <int value="1" label="IP address"/>
   <int value="2" label="Web URL"/>
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index c53e889..b3ced04f 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -3457,3 +3457,31 @@
    fun:_ZN5blink11WebViewImpl12setMainFrameEPNS_8WebFrameE
    fun:_ZN10extensions14ScopedWebFrameC1Ev
 }
+{
+   bug_581092_a
+   Memcheck:Leak
+   ...
+   fun:_ZN5blink15ContextFeatures13defaultSwitchEv
+   fun:_ZN5blink8DocumentC2ERKNS_12DocumentInitEh
+   fun:_ZN5blink12HTMLDocumentC1ERKNS_12DocumentInitEh
+   fun:_ZN5blink12HTMLDocument6createERKNS_12DocumentInitE
+   fun:_ZN5blink17DOMImplementation14createDocumentERKN3WTF6StringERKNS_12DocumentInitEb
+   fun:_ZN5blink14LocalDOMWindow14createDocumentERKN3WTF6StringERKNS_12DocumentInitEb
+   fun:_ZN5blink14LocalDOMWindow18installNewDocumentERKN3WTF6StringERKNS_12DocumentInitEb
+   fun:_ZN5blink14DocumentLoader15createWriterForEPKNS_*
+}
+{
+   bug_581092_b
+   Memcheck:Leak
+   fun:_Znw*
+   fun:_ZN5blink26RefCountedGarbageCollectedINS_15StyleFilterDataEE13makeKeepAliveEv
+   fun:_ZN5blink26RefCountedGarbageCollectedINS_15StyleFilterDataEE3refEv
+   ...
+   fun:_ZN5blink7DataRefINS_15StyleFilterDataEE4initEv
+   ...
+   fun:_ZN5blink13ComputedStyle6createEv
+   fun:_ZN5blink13StyleResolver16styleForDocumentERNS_8DocumentE
+   fun:_ZN5blink8Document6attachERKNS_4Node13AttachContextE
+   fun:_ZN5blink14LocalDOMWindow18installNewDocumentERKN3WTF6StringERKNS_12DocumentInitEb
+   fun:_ZN5blink14DocumentLoader15createWriterForEPKNS_*
+}
diff --git a/ui/arc/notification/arc_notification_item.cc b/ui/arc/notification/arc_notification_item.cc
index 694f41bd..48d386885 100644
--- a/ui/arc/notification/arc_notification_item.cc
+++ b/ui/arc/notification/arc_notification_item.cc
@@ -60,6 +60,11 @@
       item_->Click();
   }
 
+  void ButtonClick(int button_index) override {
+    if (item_)
+      item_->ButtonClick(button_index);
+  }
+
  private:
   // The destructor is private since this class is ref-counted.
   ~ArcNotificationDelegate() override {}
@@ -125,6 +130,11 @@
   DCHECK(ArcNotificationType_IsValidValue(data.type))
       << "Unsupported notification type: " << data.type;
 
+  for (size_t i = 0; i < data.buttons.size(); i++) {
+    rich_data.buttons.push_back(message_center::ButtonInfo(
+        base::UTF8ToUTF16(data.buttons.at(i)->label.get())));
+  }
+
   // The identifier of the notifier, which is used to distinguish the notifiers
   // in the message center.
   message_center::NotifierId notifier_id(
@@ -179,6 +189,11 @@
   manager_->SendNotificationClickedOnChrome(notification_key_);
 }
 
+void ArcNotificationItem::ButtonClick(int button_index) {
+  manager_->SendNotificationButtonClickedOnChrome(
+      notification_key_, button_index);
+}
+
 void ArcNotificationItem::OnImageDecoded(const SkBitmap& bitmap) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/ui/arc/notification/arc_notification_item.h b/ui/arc/notification/arc_notification_item.h
index 90247168..ee8e773 100644
--- a/ui/arc/notification/arc_notification_item.h
+++ b/ui/arc/notification/arc_notification_item.h
@@ -35,6 +35,7 @@
   // Methods called from ArcNotificationItemDelegate:
   void Close(bool by_user);
   void Click();
+  void ButtonClick(int button_index);
 
  private:
   void OnImageDecoded(const SkBitmap& bitmap);
diff --git a/ui/arc/notification/arc_notification_manager.cc b/ui/arc/notification/arc_notification_manager.cc
index a5692d54..11e7b47 100644
--- a/ui/arc/notification/arc_notification_manager.cc
+++ b/ui/arc/notification/arc_notification_manager.cc
@@ -87,4 +87,39 @@
       ->SendNotificationEventToAndroid(key, ArcNotificationEvent::BODY_CLICKED);
 }
 
+void ArcNotificationManager::SendNotificationButtonClickedOnChrome(
+    const std::string& key, int button_index) {
+  if (!items_.contains(key)) {
+    VLOG(3) << "Chrome requests to fire a click event on notification (key: "
+            << key << "), but it is gone.";
+    return;
+  }
+
+  arc::ArcNotificationEvent command;
+  switch (button_index) {
+    case 0:
+      command = ArcNotificationEvent::BUTTON1_CLICKED;
+      break;
+    case 1:
+      command = ArcNotificationEvent::BUTTON2_CLICKED;
+      break;
+    case 2:
+      command = ArcNotificationEvent::BUTTON3_CLICKED;
+      break;
+    case 3:
+      command = ArcNotificationEvent::BUTTON4_CLICKED;
+      break;
+    case 4:
+      command = ArcNotificationEvent::BUTTON5_CLICKED;
+      break;
+    default:
+      VLOG(3) << "Invalid button index (key: " << key << ", index: " <<
+          button_index << ").";
+      return;
+  }
+
+  arc_bridge_service()
+      ->notifications_instance()->SendNotificationEventToAndroid(key, command);
+}
+
 }  // namespace arc
diff --git a/ui/arc/notification/arc_notification_manager.h b/ui/arc/notification/arc_notification_manager.h
index f463fa6..1d46b8f 100644
--- a/ui/arc/notification/arc_notification_manager.h
+++ b/ui/arc/notification/arc_notification_manager.h
@@ -37,6 +37,8 @@
   // Methods called from ArcNotificationItem:
   void SendNotificationRemovedFromChrome(const std::string& key);
   void SendNotificationClickedOnChrome(const std::string& key);
+  void SendNotificationButtonClickedOnChrome(
+      const std::string& key, int button_index);
 
  private:
   const AccountId main_profile_id_;
diff --git a/ui/base/template_expressions.cc b/ui/base/template_expressions.cc
index 1d63e0c3..afddf5e0 100644
--- a/ui/base/template_expressions.cc
+++ b/ui/base/template_expressions.cc
@@ -8,29 +8,36 @@
 
 #include "base/logging.h"
 
+namespace {
+const char kLeader[] = "$i18n{";
+const size_t kLeaderSize = arraysize(kLeader) - 1;
+const char kTail[] = "}";
+const size_t kTailSize = arraysize(kTail) - 1;
+}   // namespace
+
 namespace ui {
 
 std::string ReplaceTemplateExpressions(
     base::StringPiece format_string,
-    const std::map<base::StringPiece, std::string>& substitutions) {
+    const TemplateReplacements& substitutions) {
   std::string formatted;
   const size_t kValueLengthGuess = 16;
   formatted.reserve(format_string.length() +
                     substitutions.size() * kValueLengthGuess);
   base::StringPiece::const_iterator i = format_string.begin();
   while (i < format_string.end()) {
-    if (*i == '$' && i + 2 < format_string.end() && i[1] == '{' &&
-        i[2] != '}') {
-      size_t key_start = i + strlen("${") - format_string.begin();
-      size_t key_length = format_string.find('}', key_start);
+    if (base::StringPiece(i).starts_with(kLeader)) {
+      size_t key_start = i + kLeaderSize - format_string.begin();
+      size_t key_length = format_string.find(kTail, key_start);
       if (key_length == base::StringPiece::npos)
-        NOTREACHED() << "TemplateExpression missing ending brace '}'";
+        NOTREACHED() << "TemplateExpression missing ending tag";
       key_length -= key_start;
-      base::StringPiece key(format_string.begin() + key_start, key_length);
+      std::string key(format_string.begin() + key_start, key_length);
+      DCHECK(!key.empty());
       const auto& replacement = substitutions.find(key);
       if (replacement != substitutions.end()) {
         formatted.append(replacement->second);
-        i += strlen("${") + key_length + strlen("}");
+        i += kLeaderSize + key_length + kTailSize;
         continue;
       } else {
         NOTREACHED() << "TemplateExpression key not found: " << key;
diff --git a/ui/base/template_expressions.h b/ui/base/template_expressions.h
index ff14ee1..a8ac908b 100644
--- a/ui/base/template_expressions.h
+++ b/ui/base/template_expressions.h
@@ -16,12 +16,15 @@
 
 namespace ui {
 
+// Map of strings for template replacement in |ReplaceTemplateExpressions|.
+typedef std::map<const std::string, std::string> TemplateReplacements;
+
 // Replace ${foo} in the format string with the value for the foo key in
 // |subst|.  If the key is not found in the |substitutions| that item will
 // be unaltered.
 UI_BASE_EXPORT std::string ReplaceTemplateExpressions(
     base::StringPiece format_string,
-    const std::map<base::StringPiece, std::string>& substitutions);
+    const TemplateReplacements& substitutions);
 
 }  // namespace ui
 
diff --git a/ui/base/template_expressions_unittest.cc b/ui/base/template_expressions_unittest.cc
index b56a5e7..947f2539 100644
--- a/ui/base/template_expressions_unittest.cc
+++ b/ui/base/template_expressions_unittest.cc
@@ -9,27 +9,26 @@
 namespace ui {
 
 TEST(TemplateExpressionsTest, ReplaceTemplateExpressionsPieces) {
-  std::map<base::StringPiece, std::string> substitutions;
+  TemplateReplacements substitutions;
   substitutions["test"] = "word";
   substitutions["5"] = "number";
 
-  EXPECT_EQ("${}", ReplaceTemplateExpressions("${}", substitutions));
   EXPECT_EQ("", ReplaceTemplateExpressions("", substitutions));
-  EXPECT_EQ("word", ReplaceTemplateExpressions("${test}", substitutions));
-  EXPECT_EQ("number ", ReplaceTemplateExpressions("${5} ", substitutions));
-  EXPECT_EQ(
-      "multiple: word, number.",
-      ReplaceTemplateExpressions("multiple: ${test}, ${5}.", substitutions));
+  EXPECT_EQ("word", ReplaceTemplateExpressions("$i18n{test}", substitutions));
+  EXPECT_EQ("number ", ReplaceTemplateExpressions("$i18n{5} ", substitutions));
+  EXPECT_EQ("multiple: word, number.",
+            ReplaceTemplateExpressions("multiple: $i18n{test}, $i18n{5}.",
+                                       substitutions));
 }
 
 TEST(TemplateExpressionsTest,
      ReplaceTemplateExpressionsConsecutiveDollarSignsPieces) {
-  std::map<base::StringPiece, std::string> substitutions;
+  TemplateReplacements substitutions;
   substitutions["a"] = "x";
   EXPECT_EQ("$ $$ $$$", ReplaceTemplateExpressions("$ $$ $$$", substitutions));
-  EXPECT_EQ("$x", ReplaceTemplateExpressions("$${a}", substitutions));
-  EXPECT_EQ("$$x", ReplaceTemplateExpressions("$$${a}", substitutions));
-  EXPECT_EQ("$12", ReplaceTemplateExpressions("$12", substitutions));
+  EXPECT_EQ("$x", ReplaceTemplateExpressions("$$i18n{a}", substitutions));
+  EXPECT_EQ("$$x", ReplaceTemplateExpressions("$$$i18n{a}", substitutions));
+  EXPECT_EQ("$i18n12", ReplaceTemplateExpressions("$i18n12", substitutions));
 }
 
 }  // namespace ui
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc
index d9c1d76..e83ea68 100644
--- a/ui/base/webui/web_ui_util.cc
+++ b/ui/base/webui/web_ui_util.cc
@@ -123,7 +123,7 @@
 }
 
 std::string GetWebUiCssTextDefaults(const std::string& css_template) {
-  std::map<base::StringPiece, std::string> placeholders;
+  ui::TemplateReplacements placeholders;
   placeholders["textDirection"] = GetTextDirection();
   placeholders["fontFamily"] = GetFontFamily();
   placeholders["fontSize"] = GetFontSize();
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index dff25a6..05dfaaa 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -601,12 +601,11 @@
       default:
         return DID_NOT_HANDLE;
     }
-  } else {
-    cc::InputHandlerScrollResult scroll_result =
-        input_handler_->ScrollBy(&scroll_state);
-    HandleOverscroll(scroll_point, scroll_result);
-    return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
   }
+  cc::InputHandlerScrollResult scroll_result =
+      input_handler_->ScrollBy(&scroll_state);
+  HandleOverscroll(scroll_point, scroll_result);
+  return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
 }
 
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
diff --git a/ui/views/animation/button_ink_drop_delegate.cc b/ui/views/animation/button_ink_drop_delegate.cc
index 2fa6000..40172ab 100644
--- a/ui/views/animation/button_ink_drop_delegate.cc
+++ b/ui/views/animation/button_ink_drop_delegate.cc
@@ -43,9 +43,24 @@
   ink_drop_animation_controller_->AnimateToState(state);
 }
 
+void ButtonInkDropDelegate::SetHovered(bool is_hovered) {
+  ink_drop_animation_controller_->SetHovered(is_hovered);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // ui::EventHandler:
 
+void ButtonInkDropDelegate::OnMouseEvent(ui::MouseEvent* event) {
+  switch (event->type()) {
+    case ui::ET_MOUSE_ENTERED:
+    case ui::ET_MOUSE_EXITED:
+      SetHovered(ink_drop_host_->ShouldShowInkDropHover());
+      break;
+    default:
+      return;
+  }
+}
+
 void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) {
   InkDropState current_ink_drop_state =
       ink_drop_animation_controller_->GetInkDropState();
diff --git a/ui/views/animation/button_ink_drop_delegate.h b/ui/views/animation/button_ink_drop_delegate.h
index 2e8798da..dc4ecdce 100644
--- a/ui/views/animation/button_ink_drop_delegate.h
+++ b/ui/views/animation/button_ink_drop_delegate.h
@@ -35,8 +35,10 @@
                       int small_corner_radius) override;
   void OnLayout() override;
   void OnAction(InkDropState state) override;
+  void SetHovered(bool is_hovered) override;
 
   // ui::EventHandler:
+  void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
diff --git a/ui/views/animation/ink_drop_animation.h b/ui/views/animation/ink_drop_animation.h
index abcd48c..6abc23b 100644
--- a/ui/views/animation/ink_drop_animation.h
+++ b/ui/views/animation/ink_drop_animation.h
@@ -11,7 +11,6 @@
 #include "base/time/time.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/transform.h"
 #include "ui/views/animation/ink_drop_state.h"
 #include "ui/views/views_export.h"
diff --git a/ui/views/animation/ink_drop_animation_controller.h b/ui/views/animation/ink_drop_animation_controller.h
index d4d4fce..993c852 100644
--- a/ui/views/animation/ink_drop_animation_controller.h
+++ b/ui/views/animation/ink_drop_animation_controller.h
@@ -21,8 +21,8 @@
 
 namespace views {
 
-// Pure virtual base class that manages an ink drop animation's lifetime and
-// state.
+// Pure virtual base class that manages the lifetime and state of an ink drop
+// animation as well as visual hover state feedback.
 class VIEWS_EXPORT InkDropAnimationController {
  public:
   virtual ~InkDropAnimationController() {}
@@ -33,6 +33,12 @@
   // Animates from the current InkDropState to |ink_drop_state|.
   virtual void AnimateToState(InkDropState ink_drop_state) = 0;
 
+  // Enables or disables the hover state.
+  virtual void SetHovered(bool is_hovered) = 0;
+
+  // Returns true if the hover state is enabled.
+  virtual bool IsHovered() const = 0;
+
   virtual gfx::Size GetInkDropLargeSize() const = 0;
 
   // Sets the different sizes of the ink drop.
diff --git a/ui/views/animation/ink_drop_animation_controller_factory.cc b/ui/views/animation/ink_drop_animation_controller_factory.cc
index d03593c..f1f7825 100644
--- a/ui/views/animation/ink_drop_animation_controller_factory.cc
+++ b/ui/views/animation/ink_drop_animation_controller_factory.cc
@@ -27,6 +27,8 @@
   // InkDropAnimationController:
   InkDropState GetInkDropState() const override;
   void AnimateToState(InkDropState state) override;
+  void SetHovered(bool is_hovered) override;
+  bool IsHovered() const override;
   gfx::Size GetInkDropLargeSize() const override;
   void SetInkDropSize(const gfx::Size& large_size,
                       int large_corner_radius,
@@ -35,10 +37,15 @@
   void SetInkDropCenter(const gfx::Point& center_point) override;
 
  private:
+  // Tracks whether the ink drop is hovered or not. This is used to ensure that
+  // this behaves like all other InkDropAnimationController implementations.
+  bool is_hovered_;
+
   DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerStub);
 };
 
-InkDropAnimationControllerStub::InkDropAnimationControllerStub() {}
+InkDropAnimationControllerStub::InkDropAnimationControllerStub()
+    : is_hovered_(false) {}
 
 InkDropAnimationControllerStub::~InkDropAnimationControllerStub() {}
 
@@ -46,7 +53,17 @@
   return InkDropState::HIDDEN;
 }
 
-void InkDropAnimationControllerStub::AnimateToState(InkDropState state) {}
+void InkDropAnimationControllerStub::AnimateToState(InkDropState state) {
+  SetHovered(false);
+}
+
+void InkDropAnimationControllerStub::SetHovered(bool is_hovered) {
+  is_hovered_ = is_hovered;
+}
+
+bool InkDropAnimationControllerStub::IsHovered() const {
+  return is_hovered_;
+}
 
 gfx::Size InkDropAnimationControllerStub::GetInkDropLargeSize() const {
   return gfx::Size();
diff --git a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
index 6fa3324..a00a347 100644
--- a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
+++ b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
@@ -4,12 +4,17 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/timer/timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/test/material_design_controller_test_api.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/views/animation/ink_drop_animation_controller.h"
 #include "ui/views/animation/ink_drop_animation_controller_factory.h"
+#include "ui/views/animation/ink_drop_animation_controller_impl.h"
 #include "ui/views/animation/ink_drop_host.h"
 #include "ui/views/animation/ink_drop_state.h"
 #include "ui/views/animation/test/test_ink_drop_host.h"
@@ -17,7 +22,8 @@
 namespace views {
 
 class InkDropAnimationControllerFactoryTest
-    : public testing::TestWithParam<ui::MaterialDesignController::Mode> {
+    : public testing::TestWithParam<
+          testing::tuple<ui::MaterialDesignController::Mode>> {
  public:
   InkDropAnimationControllerFactoryTest();
   ~InkDropAnimationControllerFactoryTest();
@@ -31,8 +37,14 @@
   scoped_ptr<InkDropAnimationController> ink_drop_animation_controller_;
 
  private:
+  // Extracts and returns the material design mode from the test parameters.
+  ui::MaterialDesignController::Mode GetMaterialMode() const;
+
   scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
 
+  // Required by base::Timer's.
+  scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
+
   DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerFactoryTest);
 };
 
@@ -42,7 +54,7 @@
   // initialize and cache the mode. This ensures that these tests will run from
   // a non-initialized state.
   ui::test::MaterialDesignControllerTestAPI::UninitializeMode();
-  ui::test::MaterialDesignControllerTestAPI::SetMode(GetParam());
+  ui::test::MaterialDesignControllerTestAPI::SetMode(GetMaterialMode());
   ink_drop_animation_controller_.reset(
       InkDropAnimationControllerFactory::CreateInkDropAnimationController(
           &test_ink_drop_host_)
@@ -52,6 +64,20 @@
 
   zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode(
       ui::ScopedAnimationDurationScaleMode::ZERO_DURATION));
+
+  switch (GetMaterialMode()) {
+    case ui::MaterialDesignController::NON_MATERIAL:
+      break;
+    case ui::MaterialDesignController::MATERIAL_NORMAL:
+    case ui::MaterialDesignController::MATERIAL_HYBRID:
+      // The Timer's used by the InkDropAnimationControllerImpl class require a
+      // base::ThreadTaskRunnerHandle instance.
+      scoped_refptr<base::TestMockTimeTaskRunner> task_runner(
+          new base::TestMockTimeTaskRunner);
+      thread_task_runner_handle_.reset(
+          new base::ThreadTaskRunnerHandle(task_runner));
+      break;
+  }
 }
 
 InkDropAnimationControllerFactoryTest::
@@ -59,13 +85,19 @@
   ui::test::MaterialDesignControllerTestAPI::UninitializeMode();
 }
 
+ui::MaterialDesignController::Mode
+InkDropAnimationControllerFactoryTest::GetMaterialMode() const {
+  return testing::get<0>(GetParam());
+}
+
 // Note: First argument is optional and intentionally left blank.
 // (it's a prefix for the generated test cases)
 INSTANTIATE_TEST_CASE_P(
     ,
     InkDropAnimationControllerFactoryTest,
     testing::Values(ui::MaterialDesignController::NON_MATERIAL,
-                    ui::MaterialDesignController::MATERIAL_NORMAL));
+                    ui::MaterialDesignController::MATERIAL_NORMAL,
+                    ui::MaterialDesignController::MATERIAL_HYBRID));
 
 TEST_P(InkDropAnimationControllerFactoryTest,
        VerifyAllInkDropLayersRemovedAfterDestruction) {
@@ -79,6 +111,13 @@
             ink_drop_animation_controller_->GetInkDropState());
 }
 
+TEST_P(InkDropAnimationControllerFactoryTest, HoveredStateAfterAnimateToState) {
+  ink_drop_animation_controller_->SetHovered(true);
+  ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING);
+
+  EXPECT_FALSE(ink_drop_animation_controller_->IsHovered());
+}
+
 TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickAction) {
   ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING);
   ink_drop_animation_controller_->AnimateToState(InkDropState::QUICK_ACTION);
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.cc b/ui/views/animation/ink_drop_animation_controller_impl.cc
index 74d681d8d6..2035866 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.cc
+++ b/ui/views/animation/ink_drop_animation_controller_impl.cc
@@ -4,19 +4,54 @@
 
 #include "ui/views/animation/ink_drop_animation_controller_impl.h"
 
+#include "base/timer/timer.h"
+#include "ui/compositor/layer.h"
 #include "ui/views/animation/ink_drop_animation.h"
 #include "ui/views/animation/ink_drop_host.h"
+#include "ui/views/animation/ink_drop_hover.h"
 
 namespace views {
 
+namespace {
+
+// The duration, in milliseconds, of the hover state fade in animation when it
+// is triggered by user input.
+const int kHoverFadeInFromUserInputDurationInMs = 250;
+
+// The duration, in milliseconds, of the hover state fade out animation when it
+// is triggered by user input.
+const int kHoverFadeOutFromUserInputDurationInMs = 250;
+
+// The duration, in milliseconds, of the hover state fade in animation when it
+// is triggered by an ink drop ripple animation ending.
+const int kHoverFadeInAfterAnimationDurationInMs = 250;
+
+// The duration, in milliseconds, of the hover state fade out animation when it
+// is triggered by an ink drop ripple animation starting.
+const int kHoverFadeOutBeforeAnimationDurationInMs = 0;
+
+// The amount of time in milliseconds that |hover_| should delay after a ripple
+// animation before fading in.
+const int kHoverFadeInAfterAnimationDelayInMs = 1000;
+
+}  // namespace
+
 InkDropAnimationControllerImpl::InkDropAnimationControllerImpl(
     InkDropHost* ink_drop_host)
-    : ink_drop_host_(ink_drop_host) {}
+    : ink_drop_host_(ink_drop_host),
+      ink_drop_large_corner_radius_(0),
+      ink_drop_small_corner_radius_(0),
+      root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
+      hover_after_animation_timer_(nullptr) {
+  root_layer_->set_name("InkDropAnimationControllerImpl:RootLayer");
+  ink_drop_host_->AddInkDropLayer(root_layer_.get());
+}
 
 InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() {
   // Explicitly destroy the InkDropAnimation so that this still exists if
   // views::InkDropAnimationObserver methods are called on this.
   DestroyInkDropAnimation();
+  ink_drop_host_->RemoveInkDropLayer(root_layer_.get());
 }
 
 InkDropState InkDropAnimationControllerImpl::GetInkDropState() const {
@@ -32,6 +67,18 @@
   ink_drop_animation_->AnimateToState(ink_drop_state);
 }
 
+void InkDropAnimationControllerImpl::SetHovered(bool is_hovered) {
+  SetHoveredInternal(is_hovered,
+                     is_hovered ? base::TimeDelta::FromMilliseconds(
+                                      kHoverFadeInFromUserInputDurationInMs)
+                                : base::TimeDelta::FromMilliseconds(
+                                      kHoverFadeOutFromUserInputDurationInMs));
+}
+
+bool InkDropAnimationControllerImpl::IsHovered() const {
+  return hover_ && hover_->IsVisible();
+}
+
 gfx::Size InkDropAnimationControllerImpl::GetInkDropLargeSize() const {
   return ink_drop_large_size_;
 }
@@ -49,7 +96,9 @@
   ink_drop_large_corner_radius_ = large_corner_radius;
   ink_drop_small_size_ = small_size;
   ink_drop_small_corner_radius_ = small_corner_radius;
-  ink_drop_animation_.reset();
+
+  DestroyInkDropAnimation();
+  DestroyInkDropHover();
 }
 
 void InkDropAnimationControllerImpl::SetInkDropCenter(
@@ -57,6 +106,8 @@
   ink_drop_center_ = center_point;
   if (ink_drop_animation_)
     ink_drop_animation_->SetCenterPoint(ink_drop_center_);
+  if (hover_)
+    hover_->SetCenterPoint(ink_drop_center_);
 }
 
 void InkDropAnimationControllerImpl::CreateInkDropAnimation() {
@@ -68,19 +119,40 @@
 
   ink_drop_animation_->AddObserver(this);
   ink_drop_animation_->SetCenterPoint(ink_drop_center_);
-  ink_drop_host_->AddInkDropLayer(ink_drop_animation_->root_layer());
+  root_layer_->Add(ink_drop_animation_->root_layer());
 }
 
 void InkDropAnimationControllerImpl::DestroyInkDropAnimation() {
   if (!ink_drop_animation_)
     return;
-  ink_drop_host_->RemoveInkDropLayer(ink_drop_animation_->root_layer());
+  root_layer_->Remove(ink_drop_animation_->root_layer());
   ink_drop_animation_->RemoveObserver(this);
   ink_drop_animation_.reset();
 }
 
+void InkDropAnimationControllerImpl::CreateInkDropHover() {
+  DestroyInkDropHover();
+
+  hover_.reset(
+      new InkDropHover(ink_drop_small_size_, ink_drop_small_corner_radius_));
+  hover_->SetCenterPoint(ink_drop_center_);
+  root_layer_->Add(hover_->layer());
+}
+
+void InkDropAnimationControllerImpl::DestroyInkDropHover() {
+  if (!hover_)
+    return;
+  root_layer_->Remove(hover_->layer());
+  hover_.reset();
+}
+
 void InkDropAnimationControllerImpl::InkDropAnimationStarted(
-    InkDropState ink_drop_state) {}
+    InkDropState ink_drop_state) {
+  if (IsHovered() && ink_drop_state != views::InkDropState::HIDDEN) {
+    SetHoveredInternal(false, base::TimeDelta::FromMilliseconds(
+                                  kHoverFadeOutBeforeAnimationDurationInMs));
+  }
+}
 
 void InkDropAnimationControllerImpl::InkDropAnimationEnded(
     InkDropState ink_drop_state,
@@ -94,6 +166,9 @@
       ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN);
       break;
     case views::InkDropState::HIDDEN:
+      // TODO(bruthig): Consider only starting the timer if the InkDropHost is
+      // hovered now, as oppposed to when the timer fires.
+      StartHoverAfterAnimationTimer();
       // TODO(bruthig): Investigate whether creating and destroying
       // InkDropAnimations is expensive and consider creating an
       // InkDropAnimationPool. See www.crbug.com/522175.
@@ -104,4 +179,48 @@
   }
 }
 
+void InkDropAnimationControllerImpl::SetHoveredInternal(
+    bool is_hovered,
+    base::TimeDelta animation_duration) {
+  StopHoverAfterAnimationTimer();
+
+  if (IsHovered() == is_hovered)
+    return;
+
+  if (is_hovered) {
+    if (!hover_)
+      CreateInkDropHover();
+    if (GetInkDropState() == views::InkDropState::HIDDEN) {
+      hover_->FadeIn(animation_duration);
+    }
+  } else {
+    hover_->FadeOut(animation_duration);
+  }
+}
+
+void InkDropAnimationControllerImpl::StartHoverAfterAnimationTimer() {
+  StopHoverAfterAnimationTimer();
+
+  if (!hover_after_animation_timer_)
+    hover_after_animation_timer_.reset(new base::OneShotTimer);
+
+  hover_after_animation_timer_->Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(kHoverFadeInAfterAnimationDelayInMs),
+      base::Bind(&InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired,
+                 base::Unretained(this)));
+}
+
+void InkDropAnimationControllerImpl::StopHoverAfterAnimationTimer() {
+  if (hover_after_animation_timer_)
+    hover_after_animation_timer_.reset();
+}
+
+void InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired() {
+  SetHoveredInternal(ink_drop_host_->ShouldShowInkDropHover(),
+                     base::TimeDelta::FromMilliseconds(
+                         kHoverFadeInAfterAnimationDurationInMs));
+  hover_after_animation_timer_.reset();
+}
+
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.h b/ui/views/animation/ink_drop_animation_controller_impl.h
index c533883..ff29f9f8 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.h
+++ b/ui/views/animation/ink_drop_animation_controller_impl.h
@@ -13,9 +13,15 @@
 #include "ui/views/animation/ink_drop_animation_observer.h"
 #include "ui/views/views_export.h"
 
+namespace base {
+class Timer;
+}  // namespace base
+
 namespace views {
 class InkDropAnimation;
 class InkDropHost;
+class InkDropHover;
+class InkDropAnimationControllerFactoryTest;
 
 // A functional implementation of an InkDropAnimationController.
 class VIEWS_EXPORT InkDropAnimationControllerImpl
@@ -30,6 +36,8 @@
   // InkDropAnimationController:
   InkDropState GetInkDropState() const override;
   void AnimateToState(InkDropState ink_drop_state) override;
+  void SetHovered(bool is_hovered) override;
+  bool IsHovered() const override;
   gfx::Size GetInkDropLargeSize() const override;
   void SetInkDropSize(const gfx::Size& large_size,
                       int large_corner_radius,
@@ -38,6 +46,9 @@
   void SetInkDropCenter(const gfx::Point& center_point) override;
 
  private:
+  friend class InkDropAnimationControllerFactoryTest;
+  friend class InkDropAnimationControllerImplTest;
+
   // Creates a new InkDropAnimation and sets it to |ink_drop_animation_|. If
   // |ink_drop_animation_| wasn't null then it will be destroyed using
   // DestroyInkDropAnimation().
@@ -46,12 +57,35 @@
   // Destroys the current |ink_drop_animation_|.
   void DestroyInkDropAnimation();
 
+  // Creates a new InkDropHover and sets it to |hover_|. If |hover_| wasn't null
+  // then it will be destroyed using DestroyInkDropHover().
+  void CreateInkDropHover();
+
+  // Destroys the current |hover_|.
+  void DestroyInkDropHover();
+
   // views::InkDropAnimationObserver:
   void InkDropAnimationStarted(InkDropState ink_drop_state) override;
   void InkDropAnimationEnded(InkDropState ink_drop_state,
                              InkDropAnimationEndedReason reason) override;
 
-  // The host of the ink drop.
+  // Enables or disables the hover state based on |is_hovered| and if an
+  // animation is triggered it will be scheduled to have the given
+  // |animation_duration|.
+  void SetHoveredInternal(bool is_hovered, base::TimeDelta animation_duration);
+
+  // Starts the |hover_after_animation_timer_| timer. This will stop the current
+  // |hover_after_animation_timer_| instance if it exists.
+  void StartHoverAfterAnimationTimer();
+
+  // Stops and destroys the current |hover_after_animation_timer_| instance.
+  void StopHoverAfterAnimationTimer();
+
+  // Callback for when the |hover_after_animation_timer_| fires.
+  void HoverAfterAnimationTimerFired();
+
+  // The host of the ink drop. Used to poll for information such as whether the
+  // hover should be shown or not.
   InkDropHost* ink_drop_host_;
 
   // Cached size for the ink drop's large size animations.
@@ -69,10 +103,21 @@
   // Cached center point for the ink drop.
   gfx::Point ink_drop_center_;
 
+  // The root Layer that parents the InkDropAnimation layers and the
+  // InkDropHover layers. The |root_layer_| is the one that is added and removed
+  // from the InkDropHost.
+  scoped_ptr<ui::Layer> root_layer_;
+
+  // The current InkDropHover. Lazily created using CreateInkDropHover();
+  scoped_ptr<InkDropHover> hover_;
+
   // The current InkDropAnimation. Created on demand using
   // CreateInkDropAnimation().
   scoped_ptr<InkDropAnimation> ink_drop_animation_;
 
+  // The timer used to delay the hover fade in after an ink drop animation.
+  scoped_ptr<base::Timer> hover_after_animation_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerImpl);
 };
 
diff --git a/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
new file mode 100644
index 0000000..78fd2b6
--- /dev/null
+++ b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/views/animation/ink_drop_animation_controller_impl.h"
+#include "ui/views/animation/test/test_ink_drop_host.h"
+
+namespace views {
+
+// NOTE: The InkDropAnimationControllerImpl class is also tested by the
+// InkDropAnimationControllerFactoryTest tests.
+class InkDropAnimationControllerImplTest : public testing::Test {
+ public:
+  InkDropAnimationControllerImplTest();
+  ~InkDropAnimationControllerImplTest() override;
+
+ protected:
+  TestInkDropHost ink_drop_host_;
+
+  // The test target.
+  InkDropAnimationControllerImpl ink_drop_animation_controller_;
+
+  // Used to control the tasks scheduled by the InkDropAnimationControllerImpl's
+  // Timer.
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+
+  // Required by base::Timer's.
+  scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
+
+ private:
+  // Ensures all animations complete immediately.
+  scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerImplTest);
+};
+
+InkDropAnimationControllerImplTest::InkDropAnimationControllerImplTest()
+    : ink_drop_animation_controller_(&ink_drop_host_),
+      task_runner_(new base::TestSimpleTaskRunner),
+      thread_task_runner_handle_(
+          new base::ThreadTaskRunnerHandle(task_runner_)) {
+  zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode(
+      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION));
+}
+
+InkDropAnimationControllerImplTest::~InkDropAnimationControllerImplTest() {}
+
+TEST_F(InkDropAnimationControllerImplTest, SetHoveredIsHovered) {
+  ink_drop_host_.set_should_show_hover(true);
+
+  ink_drop_animation_controller_.SetHovered(true);
+  EXPECT_TRUE(ink_drop_animation_controller_.IsHovered());
+
+  ink_drop_animation_controller_.SetHovered(false);
+  EXPECT_FALSE(ink_drop_animation_controller_.IsHovered());
+}
+
+TEST_F(InkDropAnimationControllerImplTest,
+       HoveredStateAfterHoverTimerFiresWhenHostIsHovered) {
+  ink_drop_host_.set_should_show_hover(true);
+  ink_drop_animation_controller_.AnimateToState(InkDropState::QUICK_ACTION);
+
+  EXPECT_TRUE(task_runner_->HasPendingTask());
+
+  task_runner_->RunPendingTasks();
+
+  EXPECT_TRUE(ink_drop_animation_controller_.IsHovered());
+}
+
+TEST_F(InkDropAnimationControllerImplTest,
+       HoveredStateAfterHoverTimerFiresWhenHostIsNotHovered) {
+  ink_drop_host_.set_should_show_hover(false);
+  ink_drop_animation_controller_.AnimateToState(InkDropState::QUICK_ACTION);
+
+  EXPECT_TRUE(task_runner_->HasPendingTask());
+
+  task_runner_->RunPendingTasks();
+
+  EXPECT_FALSE(ink_drop_animation_controller_.IsHovered());
+}
+
+}  // namespace views
diff --git a/ui/views/animation/ink_drop_delegate.h b/ui/views/animation/ink_drop_delegate.h
index 576b742..0bf265a 100644
--- a/ui/views/animation/ink_drop_delegate.h
+++ b/ui/views/animation/ink_drop_delegate.h
@@ -42,6 +42,9 @@
   // as well as a NONE value.
   virtual void OnAction(InkDropState state) = 0;
 
+  // Enables or disables the hover state.
+  virtual void SetHovered(bool is_hovered) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(InkDropDelegate);
 };
diff --git a/ui/views/animation/ink_drop_host.h b/ui/views/animation/ink_drop_host.h
index f50fac78..ab9f165 100644
--- a/ui/views/animation/ink_drop_host.h
+++ b/ui/views/animation/ink_drop_host.h
@@ -34,9 +34,11 @@
   virtual void RemoveInkDropLayer(ui::Layer* ink_drop_layer) = 0;
 
   // Returns the Point where the ink drop should be centered.
-  // TODO(varkha): This should be moved to InkDropConsumer.
   virtual gfx::Point CalculateInkDropCenter() const = 0;
 
+  // Returns true if the InkDropHover should be shown.
+  virtual bool ShouldShowInkDropHover() const = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(InkDropHost);
 };
diff --git a/ui/views/animation/ink_drop_hover.cc b/ui/views/animation/ink_drop_hover.cc
new file mode 100644
index 0000000..8f17674
--- /dev/null
+++ b/ui/views/animation/ink_drop_hover.cc
@@ -0,0 +1,102 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/animation/ink_drop_hover.h"
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/compositor/callback_layer_animation_observer.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
+
+namespace views {
+
+namespace {
+
+// The opacity of the hover when it is visible.
+const float kHoverVisibleOpacity = 0.08f;
+
+// The opacity of the hover when it is not visible.
+const float kHiddenOpacity = 0.0f;
+
+// The hover color.
+const SkColor kHoverColor = SK_ColorBLACK;
+
+}  // namespace
+
+InkDropHover::InkDropHover(const gfx::Size& size, int corner_radius)
+    : layer_delegate_(
+          new RoundedRectangleLayerDelegate(kHoverColor, size, corner_radius)),
+      layer_(new ui::Layer()) {
+  layer_->SetBounds(gfx::Rect(size));
+  layer_->SetFillsBoundsOpaquely(false);
+  layer_->set_delegate(layer_delegate_.get());
+  layer_->SetVisible(false);
+  layer_->SetOpacity(kHoverVisibleOpacity);
+  layer_->SetMasksToBounds(false);
+  layer_->set_name("InkDropHover:layer");
+  SetCenterPoint(gfx::Rect(size).CenterPoint());
+}
+
+InkDropHover::~InkDropHover() {}
+
+bool InkDropHover::IsVisible() const {
+  return layer_->visible();
+}
+
+void InkDropHover::FadeIn(const base::TimeDelta& duration) {
+  layer_->SetOpacity(kHiddenOpacity);
+  layer_->SetVisible(true);
+  AnimateFade(FADE_IN, duration);
+}
+
+void InkDropHover::FadeOut(const base::TimeDelta& duration) {
+  AnimateFade(FADE_OUT, duration);
+}
+
+void InkDropHover::AnimateFade(HoverAnimationType animation_type,
+                               const base::TimeDelta& duration) {
+  // The |animation_observer| will be destroyed when the
+  // AnimationStartedCallback() returns true.
+  ui::CallbackLayerAnimationObserver* animation_observer =
+      new ui::CallbackLayerAnimationObserver(
+          base::Bind(&InkDropHover::AnimationEndedCallback,
+                     base::Unretained(this), animation_type));
+
+  ui::LayerAnimator* animator = layer_->GetAnimator();
+  ui::ScopedLayerAnimationSettings animation(animator);
+  animation.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  ui::LayerAnimationElement* animation_element =
+      ui::LayerAnimationElement::CreateOpacityElement(
+          animation_type == FADE_IN ? kHoverVisibleOpacity : kHiddenOpacity,
+          duration);
+  ui::LayerAnimationSequence* animation_sequence =
+      new ui::LayerAnimationSequence(animation_element);
+  animation_sequence->AddObserver(animation_observer);
+
+  animator->StartAnimation(animation_sequence);
+
+  animation_observer->SetActive();
+}
+
+void InkDropHover::SetCenterPoint(const gfx::Point& center_point) {
+  gfx::Transform transform;
+  transform.Translate(center_point.x() - layer_->bounds().CenterPoint().x(),
+                      center_point.y() - layer_->bounds().CenterPoint().y());
+  layer_->SetTransform(transform);
+}
+
+bool InkDropHover::AnimationEndedCallback(
+    HoverAnimationType animation_type,
+    const ui::CallbackLayerAnimationObserver& observer) {
+  // AnimationEndedCallback() may be invoked when this is being destroyed and
+  // |layer_| may be null.
+  if (animation_type == FADE_OUT && layer_)
+    layer_->SetVisible(false);
+  return true;
+}
+
+}  // namespace views
diff --git a/ui/views/animation/ink_drop_hover.h b/ui/views/animation/ink_drop_hover.h
new file mode 100644
index 0000000..462bbbe
--- /dev/null
+++ b/ui/views/animation/ink_drop_hover.h
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_
+#define UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/views_export.h"
+
+namespace ui {
+class Layer;
+class CallbackLayerAnimationObserver;
+}  // namespace ui
+
+namespace views {
+class RoundedRectangleLayerDelegate;
+
+// Manages fade in/out animations for a painted Layer that is used to provide
+// visual feedback on ui::Views for mouse hover states.
+class VIEWS_EXPORT InkDropHover {
+ public:
+  InkDropHover(const gfx::Size& size, int corner_radius);
+  ~InkDropHover();
+
+  // Returns true if the hover layer is visible.
+  bool IsVisible() const;
+
+  // Fades in the hover visual over the given |duration|.
+  void FadeIn(const base::TimeDelta& duration);
+
+  // Fades out the hover visual over the given |duration|.
+  void FadeOut(const base::TimeDelta& duration);
+
+  // The root Layer that can be added in to a Layer tree.
+  ui::Layer* layer() { return layer_.get(); }
+
+  // Sets the |center_point| of the hover layer relative to its parent Layer.
+  void SetCenterPoint(const gfx::Point& center_point);
+
+ private:
+  enum HoverAnimationType { FADE_IN, FADE_OUT };
+
+  // Animates a fade in/out as specified by |animation_type| over the given
+  // |duration|.
+  void AnimateFade(HoverAnimationType animation_type,
+                   const base::TimeDelta& duration);
+
+  // The callback that will be invoked when a fade in/out animation is complete.
+  bool AnimationEndedCallback(
+      HoverAnimationType animation_type,
+      const ui::CallbackLayerAnimationObserver& observer);
+
+  // The LayerDelegate that paints the hover |layer_|.
+  scoped_ptr<RoundedRectangleLayerDelegate> layer_delegate_;
+
+  // The visual hover layer that is painted by |layer_delegate_|.
+  scoped_ptr<ui::Layer> layer_;
+
+  DISALLOW_COPY_AND_ASSIGN(InkDropHover);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_ANIMATION_INK_DROP_HOVER_H_
diff --git a/ui/views/animation/ink_drop_hover_unittest.cc b/ui/views/animation/ink_drop_hover_unittest.cc
new file mode 100644
index 0000000..08ee8b9
--- /dev/null
+++ b/ui/views/animation/ink_drop_hover_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/animation/ink_drop_hover.h"
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace views {
+namespace test {
+
+class InkDropHoverTest : public testing::Test {
+ public:
+  InkDropHoverTest();
+  ~InkDropHoverTest() override;
+
+ protected:
+  scoped_ptr<InkDropHover> CreateInkDropHover() const;
+
+ private:
+  // Enables zero duration animations during the tests.
+  scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(InkDropHoverTest);
+};
+
+InkDropHoverTest::InkDropHoverTest() {
+  zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode(
+      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION));
+}
+
+InkDropHoverTest::~InkDropHoverTest() {}
+
+scoped_ptr<InkDropHover> InkDropHoverTest::CreateInkDropHover() const {
+  return make_scoped_ptr(new InkDropHover(gfx::Size(10, 10), 3));
+}
+
+TEST_F(InkDropHoverTest, InitialStateAfterConstruction) {
+  scoped_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover();
+  EXPECT_FALSE(ink_drop_hover->IsVisible());
+}
+
+TEST_F(InkDropHoverTest, IsHoveredStateTransitions) {
+  scoped_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover();
+
+  ink_drop_hover->FadeIn(base::TimeDelta::FromMilliseconds(0));
+  EXPECT_TRUE(ink_drop_hover->IsVisible());
+
+  ink_drop_hover->FadeOut(base::TimeDelta::FromMilliseconds(0));
+  EXPECT_FALSE(ink_drop_hover->IsVisible());
+}
+
+}  // namespace test
+}  // namespace views
diff --git a/ui/views/animation/ink_drop_painted_layer_delegates.cc b/ui/views/animation/ink_drop_painted_layer_delegates.cc
index bdb7946..7d43b8a 100644
--- a/ui/views/animation/ink_drop_painted_layer_delegates.cc
+++ b/ui/views/animation/ink_drop_painted_layer_delegates.cc
@@ -8,6 +8,7 @@
 #include "ui/compositor/paint_recorder.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace views {
 
@@ -82,4 +83,34 @@
   canvas->DrawRect(gfx::Rect(size_), paint);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+//
+// RoundedRectangleLayerDelegate
+//
+
+RoundedRectangleLayerDelegate::RoundedRectangleLayerDelegate(SkColor color,
+                                                             gfx::Size size,
+                                                             int corner_radius)
+    : BasePaintedLayerDelegate(color),
+      size_(size),
+      corner_radius_(corner_radius) {}
+
+RoundedRectangleLayerDelegate::~RoundedRectangleLayerDelegate() {}
+
+gfx::PointF RoundedRectangleLayerDelegate::GetCenterPoint() const {
+  return gfx::RectF(gfx::SizeF(size_)).CenterPoint();
+}
+
+void RoundedRectangleLayerDelegate::OnPaintLayer(
+    const ui::PaintContext& context) {
+  SkPaint paint;
+  paint.setColor(color());
+  paint.setFlags(SkPaint::kAntiAlias_Flag);
+  paint.setStyle(SkPaint::kFill_Style);
+
+  ui::PaintRecorder recorder(context, size_);
+  gfx::Canvas* canvas = recorder.canvas();
+  canvas->DrawRoundRect(gfx::Rect(size_), corner_radius_, paint);
+}
+
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_painted_layer_delegates.h b/ui/views/animation/ink_drop_painted_layer_delegates.h
index feb2921..57d6f17da 100644
--- a/ui/views/animation/ink_drop_painted_layer_delegates.h
+++ b/ui/views/animation/ink_drop_painted_layer_delegates.h
@@ -81,6 +81,31 @@
   DISALLOW_COPY_AND_ASSIGN(RectangleLayerDelegate);
 };
 
+// A BasePaintedLayerDelegate that paints a rounded rectangle of a specified
+// color, size and corner radius.
+class RoundedRectangleLayerDelegate : public BasePaintedLayerDelegate {
+ public:
+  RoundedRectangleLayerDelegate(SkColor color,
+                                gfx::Size size,
+                                int corner_radius);
+  ~RoundedRectangleLayerDelegate() override;
+
+  const gfx::Size& size() const { return size_; }
+
+  // ui::LayerDelegate:
+  gfx::PointF GetCenterPoint() const override;
+  void OnPaintLayer(const ui::PaintContext& context) override;
+
+ private:
+  // The size of the rectangle.
+  gfx::Size size_;
+
+  // The radius of the corners.
+  int corner_radius_;
+
+  DISALLOW_COPY_AND_ASSIGN(RoundedRectangleLayerDelegate);
+};
+
 }  // namespace views
 
 #endif  // UI_VIEWS_ANIMATION_INK_DROP_PAINTED_LAYER_DELEGATES_H_
diff --git a/ui/views/animation/test/test_ink_drop_host.cc b/ui/views/animation/test/test_ink_drop_host.cc
index 689f997..3b46616 100644
--- a/ui/views/animation/test/test_ink_drop_host.cc
+++ b/ui/views/animation/test/test_ink_drop_host.cc
@@ -6,7 +6,8 @@
 
 namespace views {
 
-TestInkDropHost::TestInkDropHost() : num_ink_drop_layers_(0) {}
+TestInkDropHost::TestInkDropHost()
+    : num_ink_drop_layers_(0), should_show_hover_(false) {}
 
 TestInkDropHost::~TestInkDropHost() {}
 
@@ -22,4 +23,8 @@
   return gfx::Point();
 }
 
+bool TestInkDropHost::ShouldShowInkDropHover() const {
+  return should_show_hover_;
+}
+
 }  // namespace views
diff --git a/ui/views/animation/test/test_ink_drop_host.h b/ui/views/animation/test/test_ink_drop_host.h
index 7baa391..9192a116 100644
--- a/ui/views/animation/test/test_ink_drop_host.h
+++ b/ui/views/animation/test/test_ink_drop_host.h
@@ -19,14 +19,21 @@
 
   int num_ink_drop_layers() const { return num_ink_drop_layers_; }
 
+  void set_should_show_hover(bool should_show_hover) {
+    should_show_hover_ = should_show_hover;
+  }
+
   // TestInkDropHost:
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
   gfx::Point CalculateInkDropCenter() const override;
+  bool ShouldShowInkDropHover() const override;
 
  private:
   int num_ink_drop_layers_;
 
+  bool should_show_hover_;
+
   DISALLOW_COPY_AND_ASSIGN(TestInkDropHost);
 };
 
diff --git a/ui/views/button_drag_utils.cc b/ui/views/button_drag_utils.cc
index 3fb5d12..60bd6e5a 100644
--- a/ui/views/button_drag_utils.cc
+++ b/ui/views/button_drag_utils.cc
@@ -14,7 +14,10 @@
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image.h"
 #include "ui/resources/grit/ui_resources.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/label_button_border.h"
 #include "ui/views/drag_utils.h"
 #include "ui/views/resources/grit/views_resources.h"
 #include "ui/views/widget/widget.h"
@@ -49,13 +52,17 @@
   button.SetTextSubpixelRenderingEnabled(false);
   const ui::NativeTheme* theme = widget->GetNativeTheme();
   button.SetTextColor(views::Button::STATE_NORMAL,
-      theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor));
-  gfx::ShadowValues shadows(
-      10,
-      gfx::ShadowValue(gfx::Vector2d(0, 0), 1.0f,
-                       theme->GetSystemColor(
-                           ui::NativeTheme::kColorId_LabelBackgroundColor)));
-  button.SetTextShadows(shadows);
+      theme->GetSystemColor(ui::NativeTheme::kColorId_TextfieldDefaultColor));
+
+  SkColor bg_color = theme->GetSystemColor(
+      ui::NativeTheme::kColorId_TextfieldDefaultBackground);
+  if (widget->IsTranslucentWindowOpacitySupported()) {
+    button.SetTextShadows(gfx::ShadowValues(
+        10, gfx::ShadowValue(gfx::Vector2d(0, 0), 1.0f, bg_color)));
+  } else {
+    button.set_background(views::Background::CreateSolidBackground(bg_color));
+    button.SetBorder(button.CreateDefaultBorder());
+  }
   button.SetMaxSize(gfx::Size(kLinkDragImageMaxWidth, 0));
   if (icon.isNull()) {
     button.SetImage(views::Button::STATE_NORMAL,
diff --git a/ui/views/controls/button/custom_button.cc b/ui/views/controls/button/custom_button.cc
index 3ab8303..3dfc48d6 100644
--- a/ui/views/controls/button/custom_button.cc
+++ b/ui/views/controls/button/custom_button.cc
@@ -123,6 +123,8 @@
 }
 
 void CustomButton::OnEnabledChanged() {
+  // TODO(bruthig): Is there any reason we are not calling
+  // Button::OnEnabledChanged() here?
   if (enabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED))
     return;
 
@@ -130,6 +132,7 @@
     SetState(ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL);
   else
     SetState(STATE_DISABLED);
+  UpdateInkDropHoverState();
 }
 
 const char* CustomButton::GetClassName() const {
@@ -352,6 +355,10 @@
   return GetLocalBounds().CenterPoint();
 }
 
+bool CustomButton::ShouldShowInkDropHover() const {
+  return enabled() && IsMouseHovered() && !InDrag();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // CustomButton, protected:
 
@@ -408,6 +415,11 @@
   return check_mouse_position && IsMouseHovered();
 }
 
+void CustomButton::UpdateInkDropHoverState() {
+  if (ink_drop_delegate_)
+    ink_drop_delegate_->SetHovered(ShouldShowInkDropHover());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // CustomButton, View overrides (protected):
 
diff --git a/ui/views/controls/button/custom_button.h b/ui/views/controls/button/custom_button.h
index 6d23e8e..7f39408 100644
--- a/ui/views/controls/button/custom_button.h
+++ b/ui/views/controls/button/custom_button.h
@@ -106,6 +106,7 @@
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
   gfx::Point CalculateInkDropCenter() const override;
+  bool ShouldShowInkDropHover() const override;
 
  protected:
   // Construct the Button with a Listener. See comment for Button's ctor.
@@ -136,6 +137,9 @@
   // state). This does not take into account enabled state.
   bool ShouldEnterHoveredState();
 
+  // Updates the |ink_drop_delegate_|'s hover state.
+  void UpdateInkDropHoverState();
+
   InkDropDelegate* ink_drop_delegate() const { return ink_drop_delegate_; }
   void set_ink_drop_delegate(InkDropDelegate* ink_drop_delegate) {
     ink_drop_delegate_ = ink_drop_delegate;
diff --git a/ui/views/controls/button/custom_button_unittest.cc b/ui/views/controls/button/custom_button_unittest.cc
index 7f784c2..41de3466 100644
--- a/ui/views/controls/button/custom_button_unittest.cc
+++ b/ui/views/controls/button/custom_button_unittest.cc
@@ -123,6 +123,8 @@
     }
   }
 
+  void SetHovered(bool is_hovered) override {}
+
  private:
   InkDropHost* ink_drop_host_;
   bool* ink_shown_;
diff --git a/ui/views/drag_utils.cc b/ui/views/drag_utils.cc
index 2597bf29..5c64f482 100644
--- a/ui/views/drag_utils.cc
+++ b/ui/views/drag_utils.cc
@@ -14,9 +14,6 @@
 
 float GetDeviceScaleForNativeView(views::Widget* widget) {
   float device_scale = 1.0f;
-  // The following code should work on other platforms as well. But we do not
-  // yet care about device scale factor on other platforms. So to keep drag and
-  // drop behavior on other platforms un-touched, we wrap this in the #if guard.
   if (widget && widget->GetNativeView()) {
     gfx::NativeView view = widget->GetNativeView();
     gfx::Display display = gfx::Screen::GetScreenFor(view)->
diff --git a/ui/views/mus/platform_window_mus.cc b/ui/views/mus/platform_window_mus.cc
index 407d8201..7233c4c 100644
--- a/ui/views/mus/platform_window_mus.cc
+++ b/ui/views/mus/platform_window_mus.cc
@@ -5,10 +5,12 @@
 #include "ui/views/mus/platform_window_mus.h"
 
 #include "build/build_config.h"
+#include "components/bitmap_uploader/bitmap_uploader.h"
 #include "components/mus/public/cpp/property_type_converters.h"
 #include "components/mus/public/cpp/window_property.h"
 #include "components/mus/public/interfaces/window_manager.mojom.h"
 #include "mojo/converters/input_events/input_events_type_converters.h"
+#include "ui/base/view_prop.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #include "ui/views/mus/window_manager_connection.h"
 
@@ -20,6 +22,7 @@
 }  // namespace
 
 PlatformWindowMus::PlatformWindowMus(ui::PlatformWindowDelegate* delegate,
+                                     mojo::Shell* shell,
                                      mus::Window* mus_window)
     : delegate_(delegate),
       mus_window_(mus_window),
@@ -36,14 +39,20 @@
   // window and fit in the smallest sizeof(AcceleratedWidget) uint32_t
   // has this property.
 #if defined(OS_WIN) || defined(OS_ANDROID)
-  delegate_->OnAcceleratedWidgetAvailable(
-      reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++),
-      mus_window_->viewport_metrics().device_pixel_ratio);
+  gfx::AcceleratedWidget accelerated_widget =
+      reinterpret_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
 #else
-  delegate_->OnAcceleratedWidgetAvailable(
-      static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++),
-      mus_window_->viewport_metrics().device_pixel_ratio);
+  gfx::AcceleratedWidget accelerated_widget =
+      static_cast<gfx::AcceleratedWidget>(accelerated_widget_count++);
 #endif
+  delegate_->OnAcceleratedWidgetAvailable(
+      accelerated_widget, mus_window_->viewport_metrics().device_pixel_ratio);
+
+  bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(mus_window_));
+  bitmap_uploader_->Init(shell);
+  prop_.reset(new ui::ViewProp(
+      accelerated_widget, bitmap_uploader::kBitmapUploaderForAcceleratedWidget,
+      bitmap_uploader_.get()));
 }
 
 PlatformWindowMus::~PlatformWindowMus() {
diff --git a/ui/views/mus/platform_window_mus.h b/ui/views/mus/platform_window_mus.h
index fadd64f..ee124f5 100644
--- a/ui/views/mus/platform_window_mus.h
+++ b/ui/views/mus/platform_window_mus.h
@@ -16,6 +16,18 @@
 #include "ui/platform_window/platform_window.h"
 #include "ui/views/mus/mus_export.h"
 
+namespace bitmap_uploader {
+class BitmapUploader;
+}
+
+namespace mojo {
+class Shell;
+}
+
+namespace ui {
+class ViewProp;
+}
+
 namespace views {
 
 class VIEWS_MUS_EXPORT PlatformWindowMus
@@ -24,6 +36,7 @@
       public NON_EXPORTED_BASE(mus::InputEventHandler) {
  public:
   PlatformWindowMus(ui::PlatformWindowDelegate* delegate,
+                    mojo::Shell* shell,
                     mus::Window* mus_window);
   ~PlatformWindowMus() override;
 
@@ -82,6 +95,8 @@
   // True if OnWindowDestroyed() has been received.
   bool mus_window_destroyed_;
 
+  scoped_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_;
+  scoped_ptr<ui::ViewProp> prop_;
 #ifndef NDEBUG
   scoped_ptr<base::WeakPtrFactory<PlatformWindowMus>> weak_factory_;
 #endif
diff --git a/ui/views/mus/window_tree_host_mus.cc b/ui/views/mus/window_tree_host_mus.cc
index 6edb53d..ec1ab6ed 100644
--- a/ui/views/mus/window_tree_host_mus.cc
+++ b/ui/views/mus/window_tree_host_mus.cc
@@ -4,11 +4,8 @@
 
 #include "ui/views/mus/window_tree_host_mus.h"
 
-#include "components/bitmap_uploader/bitmap_uploader.h"
-#include "mojo/shell/public/interfaces/shell.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/view_prop.h"
 #include "ui/events/event.h"
 #include "ui/views/mus/input_method_mus.h"
 #include "ui/views/mus/native_widget_mus.h"
@@ -25,7 +22,8 @@
                                      mus::mojom::SurfaceType surface_type)
     : native_widget_(native_widget),
       show_state_(ui::PLATFORM_WINDOW_STATE_UNKNOWN) {
-  SetPlatformWindow(make_scoped_ptr(new PlatformWindowMus(this, window)));
+  SetPlatformWindow(
+      make_scoped_ptr(new PlatformWindowMus(this, shell, window)));
   // The location of events is already transformed, and there is no way to
   // correctly determine the reverse transform. So, don't attempt to transform
   // event locations, else the root location is wrong.
@@ -33,13 +31,6 @@
   dispatcher()->set_transform_events(false);
   compositor()->SetHostHasTransparentBackground(true);
 
-  bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(window));
-  bitmap_uploader_->Init(shell);
-  prop_.reset(
-      new ui::ViewProp(GetAcceleratedWidget(),
-                       bitmap_uploader::kBitmapUploaderForAcceleratedWidget,
-                       bitmap_uploader_.get()));
-
   input_method_.reset(new InputMethodMUS(this, window));
   SetSharedInputMethod(input_method_.get());
 }
diff --git a/ui/views/mus/window_tree_host_mus.h b/ui/views/mus/window_tree_host_mus.h
index 0116e60..f2a58b6 100644
--- a/ui/views/mus/window_tree_host_mus.h
+++ b/ui/views/mus/window_tree_host_mus.h
@@ -12,10 +12,6 @@
 
 class SkBitmap;
 
-namespace bitmap_uploader {
-class BitmapUploader;
-}
-
 namespace mojo {
 class Shell;
 }
@@ -24,11 +20,6 @@
 class Window;
 }
 
-namespace ui {
-class Compositor;
-class ViewProp;
-}
-
 namespace views {
 
 class InputMethodMUS;
@@ -44,9 +35,6 @@
   ~WindowTreeHostMus() override;
 
   PlatformWindowMus* platform_window();
-  bitmap_uploader::BitmapUploader* bitmap_uploader() {
-    return bitmap_uploader_.get();
-  }
   ui::PlatformWindowState show_state() const { return show_state_; }
 
  private:
@@ -60,8 +48,6 @@
   NativeWidgetMus* native_widget_;
   scoped_ptr<InputMethodMUS> input_method_;
   ui::PlatformWindowState show_state_;
-  scoped_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_;
-  scoped_ptr<ui::ViewProp> prop_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMus);
 };
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 15305052..3cf367b 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -27,6 +27,8 @@
       'animation/ink_drop_animation_observer.h',
       'animation/ink_drop_delegate.h',
       'animation/ink_drop_host.h',
+      'animation/ink_drop_hover.cc',
+      'animation/ink_drop_hover.h',
       'animation/ink_drop_painted_layer_delegates.cc',
       'animation/ink_drop_painted_layer_delegates.h',
       'animation/ink_drop_state.cc',
@@ -544,7 +546,9 @@
       'accessible_pane_view_unittest.cc',
       'animation/bounds_animator_unittest.cc',
       'animation/ink_drop_animation_controller_factory_unittest.cc',
+      'animation/ink_drop_animation_controller_impl_unittest.cc',
       'animation/ink_drop_animation_unittest.cc',
+      'animation/ink_drop_hover_unittest.cc',
       'bubble/bubble_border_unittest.cc',
       'bubble/bubble_delegate_unittest.cc',
       'bubble/bubble_frame_view_unittest.cc',
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index 80aaddee..54c6d29 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -209,13 +209,12 @@
     WidgetDelegate* delegate;
     bool child;
     // If TRANSLUCENT_WINDOW, the widget may be fully or partially transparent.
-    // Translucent windows may not always be supported. Use
-    // IsTranslucentWindowOpacitySupported to determine if translucent windows
-    // are supported.
     // If OPAQUE_WINDOW, we can perform optimizations based on the widget being
-    // fully opaque.  Defaults to TRANSLUCENT_WINDOW if
-    // ViewsDelegate::UseTransparentWindows().  Defaults to OPAQUE_WINDOW for
-    // non-window widgets.
+    // fully opaque.
+    // Default is based on ViewsDelegate::GetOpacityForInitParams().  Defaults
+    // to OPAQUE_WINDOW for non-window widgets.
+    // Translucent windows may not always be supported. Use
+    // IsTranslucentWindowOpacitySupported to determine whether they are.
     WindowOpacity opacity;
     bool accept_events;
     Activatable activatable;
diff --git a/ui/webui/resources/css/text_defaults.css b/ui/webui/resources/css/text_defaults.css
index 037ade7..4558782 100644
--- a/ui/webui/resources/css/text_defaults.css
+++ b/ui/webui/resources/css/text_defaults.css
@@ -16,14 +16,14 @@
  * Otherwise its $placeholders won't be expanded. */
 
 html {
-  direction: ${textDirection};
+  direction: $i18n{textDirection};
 }
 
 body {
-  font-family: ${fontFamily};
-  font-size: ${fontSize};
+  font-family: $i18n{fontFamily};
+  font-size: $i18n{fontSize};
 }
 
 button {
-  font-family: ${fontFamily};
+  font-family: $i18n{fontFamily};
 }