diff --git a/AUTHORS b/AUTHORS
index a2acf1a..a5ec7ec6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -170,6 +170,7 @@
 Gao Chun <chun.gao@intel.com>
 Gao Chun <gaochun.dev@gmail.com>
 George Liaskos <geo.liaskos@gmail.com>
+Georgy Buranov <gburanov@gmail.com>
 Giuseppe Iuculano <giuseppe@iuculano.it>
 Glenn Adams <glenn@chromium.org>
 Gnanasekar Somanathan <gnanasekar.s@samsung.com>
@@ -221,6 +222,7 @@
 Jared Shumway <jaredshumway94@gmail.com>
 Jared Sohn <jared.sohn@gmail.com>
 Jared Wein <weinjared@gmail.com>
+Jari Karppanen <jkarp@amazon.com>
 Jay Soffian <jaysoffian@gmail.com>
 Jeado Ko <haibane84@gmail.com>
 Jeongeun Kim <je_julie.kim@samsung.com>
@@ -393,6 +395,7 @@
 Pierre-Antoine LaFayette <pierre.lafayette@gmail.com>
 Po-Chun Chang <pochang0403@gmail.com>
 Pramod Begur Srinath <pramod.bs@samsung.com>
+Pranay Kumar <pranay.kumar@samsung.com>
 Prashant Hiremath <prashhir@cisco.com>
 Prashant Nevase <prashant.n@samsung.com>
 Praveen Akkiraju <praveen.anp@samsung.com>
diff --git a/BUILD.gn b/BUILD.gn
index a5d7cfd..fc5ecd4d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -8,8 +8,10 @@
 # you add a new build file, there must be some path of dependencies from this
 # file to your new one or GN won't know about it.
 
+import("//build/config/crypto.gni")
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
+import("//build/module_args/v8.gni")
 import("//remoting/remoting_host.gni")
 
 if (is_android) {
@@ -48,7 +50,6 @@
     "//chrome/test/chromedriver:chromedriver_unittests",
     "//components:components_browsertests",
     "//components:components_unittests",
-    "//components/policy:policy_templates",
     "//content/shell:content_shell",
     "//content/test:content_browsertests",
     "//content/test:content_perftests",
@@ -118,6 +119,7 @@
     "//tools/imagediff($host_toolchain)",
     "//tools/gn",
     "//tools/gn:gn_unittests",
+    "//tools/gn:generate_test_gn_data",
     "//tools/telemetry:bitmaptools($host_toolchain)",
     "//ui/accessibility:accessibility_unittests",
     "//ui/app_list:app_list_unittests",
@@ -131,11 +133,22 @@
 
   deps += root_extra_deps
 
-  # TODO(GYP): Get this working on the mac?
   if (enable_extensions && !is_mac) {
+    # TODO(GYP): Get this working on the mac?
     deps += [ "//extensions/shell:app_shell_unittests" ]
   }
 
+  if (enable_me2me_host) {
+    deps += [ "//remoting/host:remoting_me2me_host" ]
+  }
+
+  if (enable_media_router) {
+    deps += [
+      "//chrome/browser/media/router/",
+      "//chrome/browser/media/router:unit_tests",
+    ]
+  }
+
   if (enable_remoting_host) {
     deps += [
       "//remoting:remoting_unittests",
@@ -146,18 +159,6 @@
     ]
   }
 
-  if (enable_me2me_host) {
-    deps += [ "//remoting/host:remoting_me2me_host" ]
-  }
-
-  if (!is_win) {
-    deps += [ "//breakpad:symupload" ]
-  }
-
-  if (use_x11) {
-    deps += [ "//tools/xdisplaycheck" ]
-  }
-
   if (toolkit_views) {
     deps += [ "//ui/views:views_unittests" ]
   }
@@ -170,17 +171,16 @@
     deps += [ "//ui/ozone" ]
   }
 
-  if (enable_media_router) {
-    deps += [
-      "//chrome/browser/media/router/",
-      "//chrome/browser/media/router:unit_tests",
-    ]
+  if (use_x11) {
+    deps += [ "//tools/xdisplaycheck" ]
   }
 
-  if (is_win || is_mac || is_chromeos) {
-    # RLZ works on these platforms.
-    # TODO(GYP): Is this target needed, or pulled in automatically?
-    deps += [ "//rlz:rlz_lib" ]
+  if (enable_configuration_policy) {
+    deps += [ "//components/policy:policy_templates" ]
+  }
+
+  if (v8_use_external_startup_data) {
+    deps += [ "//gin:gin_v8_snapshot_fingerprint" ]
   }
 
   if (is_android) {
@@ -221,7 +221,6 @@
       "//ui/message_center:test_support",
     ]
     deps -= [
-      "//breakpad:symupload",  # TODO(GYP) ??
       "//chrome",  # TODO(GYP) ??
       "//chrome/test:browser_tests",  # TODO(GYP) ??
       "//chrome/test:interactive_ui_tests",  # TODO(GYP) ??
@@ -270,6 +269,7 @@
       "//ppapi/examples/video_encode",
       "//third_party/pdfium/samples:pdfium_test",
       "//tools/gn",
+      "//tools/gn:generate_test_gn_data",
       "//tools/gn:gn_unittests",
       "//ui/app_list:app_list_unittests",
       "//url:url_unittests",
@@ -280,6 +280,21 @@
     }
   }
 
+  if (is_linux) {  # TODO(GYP): || is_android || is_bsd?
+    deps += [
+      "//breakpad:core-2-minidump",
+      "//breakpad:minidump-2-core",
+    ]
+  }
+
+  if (is_chromeos || is_mac || is_win) {
+    deps += [
+      "//rlz:rlz_id",
+      "//rlz:rlz_lib",
+      "//rlz:rlz_unittests",
+    ]
+  }
+
   if (is_linux) {
     # The following are definitely linux-only.
     deps += [
@@ -302,33 +317,24 @@
     }
   }
 
-  if (is_linux && !is_chromeos) {
+  if (is_win || (is_linux && !is_chromeos)) {
+    # TODO(GYP): Figure out which of these should (and can) build
+    # for android/chromeos/mac/ios.
     deps += [
-      # TODO(GYP): Figure out which of these should (and can) build
-      # under which other conditions.
       "//base:base_perftests",
       "//base:base_i18n_perftests",
       "//base:check_example",
       "//base:protect_file_posix",
       "//base:build_utf8_validator_tables",
-      "//breakpad:core-2-minidump",
-      "//breakpad:minidump-2-core",
-      "//build/sanitizers:copy_llvm_symbolizer",
-      "//cc/blink:cc_blink_unittests",
       "//cc:cc_perftests",
-      "//chrome/test:chrome_app_unittests",
+      "//cc/blink:cc_blink_unittests",
       "//chrome/test:load_library_perf_tests",
       "//chrome/test:performance_browser_tests",
       "//chrome/test:sync_performance_tests",
       "//chrome/test/chromedriver:chromedriver",
       "//chrome/test/chromedriver:chromedriver_tests",
       "//chrome/tools/profile_reset:jtl_compiler",
-      "//cloud_print:cloud_print_unittests",
       "//components:components_perftests",
-      "//components/network_hints/browser",
-      "//components/webui_generator",
-      "//content/public/app:browser",
-      "//content/public/app:child",
       "//content/test:content_gl_tests",
       "//content/test:content_gl_benchmark",
       "//courgette:courgette",
@@ -337,92 +343,48 @@
       "//courgette:courgette_unittests",
       "//device:device_unittests",
       "//gin:gin_shell",
-      "//gin:gin_v8_snapshot_fingerprint",
       "//gin:gin_unittests",
       "//google_apis:google_apis_unittests",
       "//google_apis/gcm:mcs_probe",
       "//gpu:angle_unittests",
-      "//gpu:gl_tests",
-
-      # TODO(GYP): Remove this when the gles2 tests work
-      "//gpu/command_buffer/client:gles2_implementation_no_check",
-
       "//gpu:gpu_perftests",
-      "//gpu/khronos_glcts_support:khronos_glcts_test",  # TODO(GYP) crbug.com/471903 to make this complete.
+      "//gpu:gl_tests",
       "//ipc:ipc_perftests",
       "//media:ffmpeg_regression_tests",  # TODO(GYP) this should be conditional on media_use_ffmpeg
       "//media:media_perftests",
-      "//media/cast:cast_benchmarks",
       "//media/cast:generate_barcode_video",
       "//media/cast:generate_timecode_audio",
-      "//media/cast:tap_proxy",
-      "//mojo/application",
       "//net:crash_cache",
       "//net:crl_set_dump",
       "//net:dns_fuzz_stub",
+      "//net:dump_cache",
       "//net:gdig",
       "//net:get_server_time",
       "//net:net_watcher",  # TODO(GYP): This should be conditional on use_v8_in_net
+      "//net:run_testserver",
       "//net:stress_cache",
       "//net:tld_cleanup",
-      "//net:run_testserver",
-      "//net:dump_cache",
       "//ppapi:pepper_hash_for_uma",
-      "//ppapi:ppapi_perftests",  # TODO(GYP): Are there other ppapi_* test targets?
-      "//skia:filter_fuzz_stub",
-      "//skia:image_operations_bench",
+      "//ppapi:ppapi_perftests",
       "//sync:run_sync_testserver",
       "//sync:sync_endtoend_tests",
-      "//sync/tools:sync_client",
-      "//sync/tools:sync_listen_notifications",
-      "//testing/gmock:gmock_main",
       "//third_party/codesighs:maptsvdifftool",
-      "//third_party/libphonenumber:libphonenumber_unittests",
-      "//third_party/mojo/src/mojo/edk/test:mojo_public_system_perftests",
-      "//tools/gn:generate_test_gn_data",
-      "//tools/perf/clear_system_cache",
-      "//ui/keyboard:keyboard_unittests",
-      "//ui/message_center:message_center_unittests",
-      "//ui/snapshot:snapshot_unittests",
-      "//ui/views/examples:views_examples_with_content_exe",
-
-      # "//v8:v8_snapshot",  # TODO(GYP): visibility?
-      # "//v8:postmortem-metadata",  # TODO(GYP): visibility?
-
-      "//third_party/codesighs:nm2tsv",
       "//third_party/leveldatabase:env_chromium_unittests",
       "//third_party/libaddressinput:libaddressinput_unittests",
-      "//third_party/sqlite:sqlite_shell",
+      "//third_party/libphonenumber:libphonenumber_unittests",
       "//ui/compositor:compositor_unittests",
     ]
 
-    if (current_toolchain == host_toolchain) {
-      # Do not build the breakpad utilities in cross-compiles.
-      deps += [
-        "//breakpad:dump_syms",
-        "//breakpad:microdump_stackwalk",
-        "//breakpad:minidump_dump",
-        "//breakpad:minidump_stackwalk",
-      ]
-    }
-
     if (enable_extensions) {
       deps += [ "//extensions/shell:app_shell" ]
     }
 
     if (enable_nacl) {
-      deps += [
-        "//components/nacl:nacl_loader_unittests",
-        "//remoting:remoting_key_tester",
-      ]
+      deps += [ "//components/nacl:nacl_loader_unittests" ]
     }
 
-    if (!is_debug && !is_component_build) {
-      deps += [ "//chrome/tools/service_discovery_sniffer" ]
-    }
-
-    if (toolkit_views) {
-      deps += [ "//ui/app_list:app_list_demo" ]
+    if (enable_nacl && enable_remoting) {
+      deps += [ "//remoting:remoting_key_tester" ]
     }
 
     if (use_ash) {
@@ -440,6 +402,63 @@
         "//ui/aura:demo",
       ]
     }
+  }
+
+  if (is_linux && !is_chromeos) {
+    deps += [
+      # TODO(GYP): Figure out which of these should (and can) build
+      # under which other conditions.
+      "//build/sanitizers:copy_llvm_symbolizer",
+      "//chrome/test:chrome_app_unittests",
+      "//cloud_print:cloud_print_unittests",
+      "//components/network_hints/browser",
+      "//components/webui_generator",
+      "//content/public/app:browser",
+      "//content/public/app:child",
+
+      # TODO(GYP): Remove this when the gles2 tests work
+      "//gpu/command_buffer/client:gles2_implementation_no_check",
+
+      "//gpu/khronos_glcts_support:khronos_glcts_test",  # TODO(GYP) crbug.com/471903 to make this complete.
+      "//media/cast:cast_benchmarks",
+      "//media/cast:tap_proxy",
+      "//mojo/application",
+      "//skia:filter_fuzz_stub",
+      "//skia:image_operations_bench",
+      "//sync/tools:sync_client",
+      "//sync/tools:sync_listen_notifications",
+      "//testing/gmock:gmock_main",
+      "//third_party/mojo/src/mojo/edk/test:mojo_public_system_perftests",
+      "//tools/perf/clear_system_cache",
+      "//ui/keyboard:keyboard_unittests",
+      "//ui/message_center:message_center_unittests",
+      "//ui/snapshot:snapshot_unittests",
+      "//ui/views/examples:views_examples_with_content_exe",
+
+      # "//v8:v8_snapshot",  # TODO(GYP): visibility?
+      # "//v8:postmortem-metadata",  # TODO(GYP): visibility?
+
+      "//third_party/codesighs:nm2tsv",
+      "//third_party/sqlite:sqlite_shell",
+    ]
+
+    if (current_toolchain == host_toolchain) {
+      # Do not build the breakpad utilities in cross-compiles.
+      deps += [
+        "//breakpad:dump_syms",
+        "//breakpad:microdump_stackwalk",
+        "//breakpad:minidump_dump",
+        "//breakpad:minidump_stackwalk",
+      ]
+    }
+
+    if (!is_debug && !is_component_build) {
+      deps += [ "//chrome/tools/service_discovery_sniffer" ]
+    }
+
+    if (toolkit_views) {
+      deps += [ "//ui/app_list:app_list_demo" ]
+    }
 
     if (use_x11) {
       deps += [ "//media:player_x11" ]
@@ -453,7 +472,6 @@
     deps += [
       "//breakpad:crash_inspector",
       "//breakpad:dump_syms",
-      "//breakpad:symupload",
       "//third_party/apple_sample_code",
       "//third_party/molokocacao",
     ]
@@ -506,12 +524,37 @@
       "//ui/app_list:app_list_unittests",  # TODO(GYP)
       "//ui/gfx:gfx_unittests",  # TODO(GYP)
     ]
-  } else if (is_win) {
-    deps += [ "//ui/metro_viewer" ]
+  }
+
+  if (is_win) {
+    deps += [
+      "//base:pe_image_test",
+      "//chrome_elf:chrome_elf_unittests",
+      "//chrome_elf:dll_hash_main",
+
+      # "//components/crash/tools:crash_service", TODO(GYP) - doesn't fully build yet.
+      "//components/wifi:wifi_test",
+      "//net:quic_client",
+      "//net:quic_server",
+      "//sandbox/win:pocdll",
+      "//sandbox/win:sandbox_poc",
+      "//sandbox/win:sbox_integration_tests",
+      "//sandbox/win:sbox_unittests",
+      "//sandbox/win:sbox_validation_tests",
+      "//testing/gtest:gtest_main",
+      "//third_party/codesighs:msmap2tsv",
+      "//third_party/pdfium/samples:pdfium_diff",
+      "//ui/metro_viewer",
+    ]
     deps -= [
       "//crypto:crypto_unittests",  # TODO(GYP)
       "//net:net_unittests",  # TODO(GYP)
     ]
+  } else {
+    if (!is_android) {
+      # TODO(GYP): Make this work on android also.
+      deps += [ "//breakpad:symupload" ]
+    }
   }
 }
 
diff --git a/DEPS b/DEPS
index da52a20..6e7977d 100644
--- a/DEPS
+++ b/DEPS
@@ -34,7 +34,7 @@
   'llvm_url': 'http://src.chromium.org/llvm-project',
   'llvm_git': 'https://llvm.googlesource.com',
   'webkit_trunk': 'http://src.chromium.org/blink/trunk',
-  'webkit_revision': '846a3f3643c4bdbbf5b85978852f61c608094376', # from svn revision 193629
+  'webkit_revision': 'bb7e4b1448d1fa2d1b55dcbb130aefb8f7f070f1', # from svn revision 193681
   'chromium_git': 'https://chromium.googlesource.com',
   'chromiumos_git': 'https://chromium.googlesource.com/chromiumos',
   'pdfium_git': 'https://pdfium.googlesource.com',
@@ -42,12 +42,12 @@
   'boringssl_git': 'https://boringssl.googlesource.com',
   'libvpx_revision': '1fff3e3550a605f7edf72280ed8fcaa83eeb586e',
   'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
-  'skia_revision': '64b309c35bfddb8382c3e38f1d8ff7efe4098cab',
+  'skia_revision': '7ef63c85c5891ca59906e3c783a7f954be3f7f62',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and V8 without interference from each other.
   'v8_branch': 'trunk',
-  'v8_revision': '065a2495d4d592d0c2af93a49543d8ea210332d2',
+  'v8_revision': '441e976ecca14a1603bee4211cc821d468367291',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling WebRTC
   # and V8 without interference from each other.
@@ -66,7 +66,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '1ed2ceb70476b135a3dedbb45549d6b3bc6ecdea',
+  'pdfium_revision': 'eddab4425614e49146f904f00da4a664ba4b581b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -78,7 +78,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nss
   # and whatever else without interference from each other.
-  'nss_revision': 'd1edb68688b91a380fb2025b3420fad68db54ed6', # from svn revision 294684
+  'nss_revision': '95068068df410e398ac221a9195c999b22bd63e9', # from svn revision 294785
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -210,7 +210,7 @@
    Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '104f872faf2cd809cdada885a1e39be85e5b3316',
 
   'src/third_party/libjingle/source/talk':
-    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + '61d33e57aae9faa4c8bf8e788c66df0f749a9465',
+    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + 'd872289cd2805dba53009a17543221ce50220ea1',
 
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/usrsctplib.git' + '@' + '36444a999739e9e408f8f587cb4c3ffeef2e50ac', # from svn revision 9215
@@ -234,7 +234,7 @@
    Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'cc8ca89e85299fd62fdaf6d494c8963e17d188dc',
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '5ffd4ec78c21a89fd4926d561315e3c699a5021a',
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -243,7 +243,7 @@
     Var('chromium_git') + '/external/github.com/open-source-parsers/jsoncpp.git' + '@' + 'f572e8e42e22cfcf5ab0aea26574f408943edfa4', # from svn 248
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/external/libyuv.git' + '@' + 'd204db647e591ccf0e2589236ecea90330d65a66', # from svn revision 1171
+    Var('chromium_git') + '/external/libyuv.git' + '@' + '32ad6e0e12fec058efdf85a5a4b32ed657f27c36', # from svn revision 1368
 
   'src/third_party/smhasher/src':
     Var('chromium_git') + '/external/smhasher.git' + '@' + 'e87738e57558e0ec472b2fc3a643b838e5b6e88f',
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index db47f02..d894e7e 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -60,8 +60,6 @@
       ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
   std::string locale = l10n_util::GetApplicationLocale(std::string()) + ".pak";
   if (AwAssets::OpenAsset(locale, &pak_fd, &pak_off, &pak_len)) {
-    VLOG(0) << "Load from apk succesful, fd=" << pak_fd << " off=" << pak_off
-            << " len=" << pak_len;
     ui::ResourceBundle::CleanupSharedInstance();
     ui::ResourceBundle::InitSharedInstanceWithPakFileRegion(
         base::File(pak_fd), base::MemoryMappedFile::Region(pak_off, pak_len));
@@ -73,8 +71,6 @@
   // Try to directly mmap the webviewchromium.pak from the apk. Fall back to
   // load from file, using PATH_SERVICE, otherwise.
   if (AwAssets::OpenAsset("webviewchromium.pak", &pak_fd, &pak_off, &pak_len)) {
-    VLOG(0) << "Loading webviewchromium.pak from, fd:" << pak_fd
-            << " off:" << pak_off << " len:" << pak_len;
     ui::ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion(
         base::File(pak_fd),
         base::MemoryMappedFile::Region(pak_off, pak_len),
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
index 2246be3..168ea65 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentViewClient.java
@@ -5,6 +5,7 @@
 package org.chromium.android_webview;
 
 import android.annotation.SuppressLint;
+import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -189,4 +190,23 @@
     public boolean isExternalFlingActive() {
         return mAwContents.isFlingActive();
     }
+
+    @Override
+    public boolean doesPerformWebSearch() {
+        return true;
+    }
+
+    @Override
+    public void performWebSearch(String query) {
+        Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
+        i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
+        i.putExtra(SearchManager.QUERY, query);
+        i.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
+        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        try {
+            mContext.startActivity(i);
+        } catch (android.content.ActivityNotFoundException ex) {
+            // If no app handles it, do nothing.
+        }
+    }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 3079e33..4e8baa3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -234,8 +234,8 @@
     private boolean mHasRequestedVisitedHistoryFromClient;
     // TODO(boliu): This should be in a global context, not per webview.
     private final double mDIPScale;
-    // Whether the WebView has attempted to do any load (including uncommitted loads).
-    private boolean mDidAttemptLoad = false;
+    // Whether this WebView is a popup.
+    private boolean mIsPopupWindow = false;
 
     // The base background color, i.e. not accounting for any CSS body from the current page.
     private int mBaseBackgroundColor = Color.WHITE;
@@ -961,8 +961,7 @@
         if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused);
         if (wasFocused) onFocusChanged(true, 0, null);
 
-        // Popups are always assumed as having made a load attempt.
-        mDidAttemptLoad = true;
+        mIsPopupWindow = true;
 
         // Restore injected JavaScript interfaces.
         for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) {
@@ -2301,10 +2300,8 @@
         nativeInsertVisualStateCallback(mNativeAwContents, requestId, callback);
     }
 
-    public boolean getDidAttemptLoad() {
-        if (mDidAttemptLoad) return mDidAttemptLoad;
-        mDidAttemptLoad = mWebContentsObserver.hasStartedNonApiProvisionalLoadInMainFrame();
-        return mDidAttemptLoad;
+    public boolean isPopupWindow() {
+        return mIsPopupWindow;
     }
 
     //--------------------------------------------------------------------------------------------
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 509a6de..675f5b1 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -219,8 +219,8 @@
     @Override
     public void navigationStateChanged(int flags) {
         if ((flags & InvalidateTypes.URL) != 0
-                && mAwContents.hasAccessedInitialDocument()
-                && mAwContents.getDidAttemptLoad()) {
+                && mAwContents.isPopupWindow()
+                && mAwContents.hasAccessedInitialDocument()) {
             // Hint the client to show the last committed url, as it may be unsafe to show
             // the pending entry.
             String url = mAwContents.getLastCommittedUrl();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
index 2652e851..de5dfb13 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -6,11 +6,9 @@
 
 import org.chromium.android_webview.AwContents.VisualStateCallback;
 import org.chromium.base.ThreadUtils;
-import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.net.NetError;
-import org.chromium.ui.base.PageTransition;
 
 import java.lang.ref.WeakReference;
 
@@ -24,7 +22,6 @@
     // and should be found and cleaned up.
     private final WeakReference<AwContents> mAwContents;
     private final WeakReference<AwContentsClient> mAwContentsClient;
-    private boolean mStartedNonApiProvisionalLoadInMainFrame = false;
 
     public AwWebContentsObserver(
             WebContents webContents, AwContents awContents, AwContentsClient awContentsClient) {
@@ -33,10 +30,6 @@
         mAwContentsClient = new WeakReference<>(awContentsClient);
     }
 
-    boolean hasStartedNonApiProvisionalLoadInMainFrame() {
-        return mStartedNonApiProvisionalLoadInMainFrame;
-    }
-
     @Override
     public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
         AwContentsClient client = mAwContentsClient.get();
@@ -103,23 +96,4 @@
         if (client == null) return;
         client.doUpdateVisitedHistory(url, isReload);
     }
-
-    @Override
-    public void didStartProvisionalLoadForFrame(
-            long frameId,
-            long parentFrameId,
-            boolean isMainFrame,
-            String validatedUrl,
-            boolean isErrorPage,
-            boolean isIframeSrcdoc) {
-        if (!isMainFrame) return;
-        AwContents awContents = mAwContents.get();
-        if (awContents != null) {
-            NavigationEntry pendingEntry = awContents.getNavigationController().getPendingEntry();
-            if (pendingEntry != null
-                    && (pendingEntry.getTransition() & PageTransition.FROM_API) == 0) {
-                mStartedNonApiProvisionalLoadInMainFrame = true;
-            }
-        }
-    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
index a431f1a..3ec8c45 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
@@ -275,6 +275,18 @@
                 TimeUnit.MILLISECONDS);
     }
 
+    /**
+     * Stops loading on the UI thread.
+     */
+    public void stopLoading(final AwContents awContents) {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.stopLoading();
+            }
+        });
+    }
+
     public void waitForVisualStateCallback(final AwContents awContents) throws Exception {
         final CallbackHelper ch = new CallbackHelper();
         final int chCount = ch.getCallCount();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
index a574fc0..6336c1cd 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
@@ -15,6 +15,8 @@
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.net.test.util.TestWebServer;
 
+import java.util.concurrent.CountDownLatch;
+
 /**
  * Tests for the ContentViewClient.onPageFinished() method.
  */
@@ -39,7 +41,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedPassesCorrectUrl() throws Throwable {
+    public void testPassesCorrectUrl() throws Throwable {
         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                 mContentsClient.getOnPageFinishedHelper();
 
@@ -53,7 +55,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedCalledAfterError() throws Throwable {
+    public void testCalledAfterError() throws Throwable {
         class LocalTestClient extends TestAwContentsClient {
             private boolean mIsOnReceivedErrorCalled = false;
             private boolean mIsOnPageFinishedCalled = false;
@@ -110,7 +112,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedCalledAfterRedirectedUrlIsOverridden() throws Throwable {
+    public void testCalledAfterRedirectedUrlIsOverridden() throws Throwable {
         /*
          * If url1 is redirected url2, and url2 load is overridden, onPageFinished should still be
          * called for url2.
@@ -149,7 +151,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledForValidSubresources() throws Throwable {
+    public void testNotCalledForValidSubresources() throws Throwable {
         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                 mContentsClient.getOnPageFinishedHelper();
 
@@ -188,7 +190,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledForHistoryApi() throws Throwable {
+    public void testNotCalledForHistoryApi() throws Throwable {
         TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
                 mContentsClient.getOnPageFinishedHelper();
         enableJavaScriptOnUiThread(mAwContents);
@@ -227,13 +229,13 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedCalledForHrefNavigations() throws Throwable {
+    public void testCalledForHrefNavigations() throws Throwable {
         doTestOnPageFinishedCalledForHrefNavigations(false);
     }
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedCalledForHrefNavigationsWithBaseUrl() throws Throwable {
+    public void testCalledForHrefNavigationsWithBaseUrl() throws Throwable {
         doTestOnPageFinishedCalledForHrefNavigations(true);
     }
 
@@ -282,7 +284,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledOnDomModificationForBlankWebView() throws Throwable {
+    public void testNotCalledOnDomModificationForBlankWebView() throws Throwable {
         TestWebServer webServer = TestWebServer.start();
         try {
             doTestOnPageFinishedNotCalledOnDomMutation(webServer, null);
@@ -293,7 +295,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledOnDomModificationAfterNonCommittedLoadFromApi()
+    public void testNotCalledOnDomModificationAfterNonCommittedLoadFromApi()
             throws Throwable {
         enableJavaScriptOnUiThread(mAwContents);
         TestWebServer webServer = TestWebServer.start();
@@ -308,7 +310,57 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledOnDomModificationAfterLoadUrl() throws Throwable {
+    public void testNotCalledOnDomModificationWithJavascriptUrlAfterNonCommittedLoadFromApi()
+            throws Throwable {
+        enableJavaScriptOnUiThread(mAwContents);
+        TestWebServer webServer = TestWebServer.start();
+        try {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final String url = webServer.setResponseWithRunnableAction(
+                    "/about.html", CommonResources.ABOUT_HTML, null,
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                latch.await(WAIT_TIMEOUT_MS,
+                                        java.util.concurrent.TimeUnit.MILLISECONDS);
+                            } catch (InterruptedException e) {
+                                fail("Caught InterruptedException " + e);
+                            }
+                        }
+                    });
+            TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
+                    mContentsClient.getOnPageFinishedHelper();
+            final int onPageFinishedCallCount = onPageFinishedHelper.getCallCount();
+            loadUrlAsync(mAwContents, url);
+            loadUrlAsync(mAwContents,
+                    "javascript:(function(){document.body.innerHTML='Hello,%20World!';})()");
+            stopLoading(mAwContents);
+            // We now have 3 possible outcomes:
+            //  - the good one -- onPageFinished only fires for the first load;
+            //  - two bad ones:
+            //      - onPageFinished fires for the dom mutation, then for the first load. (1)
+            //      - onPageFinished fires for the first load, then for the dom mutation; (2)
+            // We verify that (1) doesn't happen with the code below. Then we load a sync page,
+            // and make sure that we are getting onPageFinished for the sync page, not due
+            // to the dom mutation, thus verifying that (2) doesn't happen as well.
+            onPageFinishedHelper.waitForCallback(onPageFinishedCallCount);
+            assertEquals(url, onPageFinishedHelper.getUrl());
+            assertEquals(onPageFinishedCallCount + 1, onPageFinishedHelper.getCallCount());
+            latch.countDown();  // Release the server.
+            final String syncUrl = webServer.setResponse("/sync.html", "", null);
+            loadUrlAsync(mAwContents, syncUrl);
+            onPageFinishedHelper.waitForCallback(onPageFinishedCallCount + 1);
+            assertEquals(syncUrl, onPageFinishedHelper.getUrl());
+            assertEquals(onPageFinishedCallCount + 2, onPageFinishedHelper.getCallCount());
+        } finally {
+            webServer.shutdown();
+        }
+    }
+
+    @MediumTest
+    @Feature({"AndroidWebView"})
+    public void testNotCalledOnDomModificationAfterLoadUrl() throws Throwable {
         TestWebServer webServer = TestWebServer.start();
         try {
             final String testUrl =
@@ -322,7 +374,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedNotCalledOnDomModificationAfterLoadData()
+    public void testNotCalledOnDomModificationAfterLoadData()
             throws Throwable {
         TestWebServer webServer = TestWebServer.start();
         try {
@@ -358,7 +410,7 @@
 
     @MediumTest
     @Feature({"AndroidWebView"})
-    public void testOnPageFinishedCalledAfter204Reply() throws Throwable {
+    public void testCalledAfter204Reply() throws Throwable {
         TestWebServer webServer = TestWebServer.start();
         try {
             final String url = webServer.setResponseWithNoContentStatus("/page.html");
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
index c5ab9dae..6007de0 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedError2Test.java
@@ -359,7 +359,7 @@
                 mContentsClient.getOnPageFinishedHelper();
         final int onPageFinishedCallCount = onPageFinishedHelper.getCallCount();
         loadUrlAsync(mAwContents, url);
-        mAwContents.stopLoading();
+        stopLoading(mAwContents);
         onPageFinishedHelper.waitForCallback(onPageFinishedCallCount,
                 1 /* numberOfCallsToWaitFor */,
                 WAIT_TIMEOUT_MS,
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 137f61c..b9f134e3 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -894,7 +894,7 @@
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
-if (is_linux && !is_chromeos) {
+if (is_win || (is_linux && !is_chromeos)) {
   # TODO(GYP): Figure out which of these work and are needed on other platforms.
   test("base_perftests") {
     sources = [
@@ -1250,6 +1250,7 @@
     "test/histogram_tester_unittest.cc",
     "test/test_reg_util_win_unittest.cc",
     "test/trace_event_analyzer_unittest.cc",
+    "test/user_action_tester_unittest.cc",
     "threading/non_thread_safe_unittest.cc",
     "threading/platform_thread_unittest.cc",
     "threading/sequenced_worker_pool_unittest.cc",
@@ -1374,7 +1375,6 @@
     set_sources_assignment_filter([])
     sources += [ "debug/proc_maps_linux_unittest.cc" ]
     set_sources_assignment_filter(sources_assignment_filter)
-    sources -= [ "files/file_path_watcher_unittest.cc" ]
   }
 
   # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
diff --git a/base/android/build_info.h b/base/android/build_info.h
index e4e84e8..9d73bdb 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -18,8 +18,6 @@
 // This enumeration maps to the values returned by BuildInfo::sdk_int(),
 // indicating the Android release associated with a given SDK version.
 enum SdkVersion {
-  SDK_VERSION_ICE_CREAM_SANDWICH = 14,
-  SDK_VERSION_ICE_CREAM_SANDWICH_MR1 = 15,
   SDK_VERSION_JELLY_BEAN = 16,
   SDK_VERSION_JELLY_BEAN_MR1 = 17,
   SDK_VERSION_JELLY_BEAN_MR2 = 18,
diff --git a/base/android/java/src/org/chromium/base/AccessedByNative.java b/base/android/java/src/org/chromium/base/annotations/AccessedByNative.java
similarity index 93%
rename from base/android/java/src/org/chromium/base/AccessedByNative.java
rename to base/android/java/src/org/chromium/base/annotations/AccessedByNative.java
index 3e163fc..6df7c110 100644
--- a/base/android/java/src/org/chromium/base/AccessedByNative.java
+++ b/base/android/java/src/org/chromium/base/annotations/AccessedByNative.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.base;
+package org.chromium.base.annotations;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff --git a/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java b/base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java
similarity index 96%
rename from base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
rename to base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java
index d5e9b74..c0abcbe6 100644
--- a/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
+++ b/base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.base;
+package org.chromium.base.annotations;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff --git a/base/android/java/src/org/chromium/base/UsedByReflection.java b/base/android/java/src/org/chromium/base/annotations/UsedByReflection.java
similarity index 95%
rename from base/android/java/src/org/chromium/base/UsedByReflection.java
rename to base/android/java/src/org/chromium/base/annotations/UsedByReflection.java
index 7d18fb0..a2af704 100644
--- a/base/android/java/src/org/chromium/base/UsedByReflection.java
+++ b/base/android/java/src/org/chromium/base/annotations/UsedByReflection.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.base;
+package org.chromium.base.annotations;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Target;
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index dbeb758..7e509987 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -10,10 +10,10 @@
 import android.os.Parcelable;
 import android.util.Log;
 
-import org.chromium.base.AccessedByNative;
 import org.chromium.base.CalledByNative;
 import org.chromium.base.SysUtils;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.AccessedByNative;
 
 import java.io.FileNotFoundException;
 import java.util.HashMap;
diff --git a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
index 732fe6e..89bee99 100644
--- a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
+++ b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
@@ -6,11 +6,11 @@
 
 import android.graphics.Rect;
 
-import org.chromium.base.AccessedByNative;
 import org.chromium.base.CalledByNative;
-import org.chromium.base.CalledByNativeUnchecked;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.NativeClassQualifiedName;
+import org.chromium.base.annotations.AccessedByNative;
+import org.chromium.base.annotations.CalledByNativeUnchecked;
 
 import java.util.ArrayList;
 import java.util.Iterator;
diff --git a/base/base.gyp b/base/base.gyp
index 50a57ea..b9362b4 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -632,6 +632,7 @@
         'test/test_pending_task_unittest.cc',
         'test/test_reg_util_win_unittest.cc',
         'test/trace_event_analyzer_unittest.cc',
+        'test/user_action_tester_unittest.cc',
         'threading/non_thread_safe_unittest.cc',
         'threading/platform_thread_unittest.cc',
         'threading/sequenced_worker_pool_unittest.cc',
@@ -828,7 +829,6 @@
         ['OS == "android"', {
           'sources/': [
             ['include', '^debug/proc_maps_linux_unittest\\.cc$'],
-            ['exclude', '^files/file_path_watcher_unittest\\.cc$'],
           ],
         }],
         # Enable more direct string conversions on platforms with native utf8
@@ -1011,6 +1011,8 @@
         'test/trace_event_analyzer.h',
         'test/trace_to_file.cc',
         'test/trace_to_file.h',
+        'test/user_action_tester.cc',
+        'test/user_action_tester.h',
         'test/values_test_util.cc',
         'test/values_test_util.h',
       ],
diff --git a/base/files/file_path_watcher.cc b/base/files/file_path_watcher.cc
index b173541..59ae7059 100644
--- a/base/files/file_path_watcher.cc
+++ b/base/files/file_path_watcher.cc
@@ -32,7 +32,7 @@
   // FSEvents isn't available on iOS and is broken on OSX 10.6 and earlier.
   // See http://crbug.com/54822#c31
   return mac::IsOSLionOrLater();
-#elif defined(OS_WIN) || defined(OS_LINUX)
+#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)
   return true;
 #else
   return false;
diff --git a/base/files/file_path_watcher_unittest.cc b/base/files/file_path_watcher_unittest.cc
index bc7e6307..0e1c4672 100644
--- a/base/files/file_path_watcher_unittest.cc
+++ b/base/files/file_path_watcher_unittest.cc
@@ -31,6 +31,10 @@
 #include "base/threading/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/path_utils.h"
+#endif  // defined(OS_ANDROID)
+
 namespace base {
 
 namespace {
@@ -150,7 +154,17 @@
     // Create a separate file thread in order to test proper thread usage.
     base::Thread::Options options(MessageLoop::TYPE_IO, 0);
     ASSERT_TRUE(file_thread_.StartWithOptions(options));
+#if defined(OS_ANDROID)
+    // Watching files is only permitted when all parent directories are
+    // accessible, which is not the case for the default temp directory
+    // on Android which is under /data/data.  Use /sdcard instead.
+    // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
+    FilePath parent_dir;
+    ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
+#else   // defined(OS_ANDROID)
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+#endif  // defined(OS_ANDROID)
     collector_ = new NotificationCollector();
   }
 
@@ -526,9 +540,16 @@
   ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
   ASSERT_TRUE(WaitForEvents());
 
+// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
+// "fuse" file system, while /data uses "ext4".  Running these tests in /data
+// would be preferable and allow testing file attributes and symlinks.
+// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
+// the |temp_dir_| in /data.
+#if !defined(OS_ANDROID)
   // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
   ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1));
   ASSERT_TRUE(WaitForEvents());
+#endif
 
   // Delete "$dir/subdir/subdir_file1".
   ASSERT_TRUE(base::DeleteFile(subdir_file1, false));
@@ -541,6 +562,14 @@
 }
 
 #if defined(OS_POSIX)
+#if defined(OS_ANDROID)
+// Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
+// "fuse" file system, while /data uses "ext4".  Running these tests in /data
+// would be preferable and allow testing file attributes and symlinks.
+// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
+// the |temp_dir_| in /data.
+#define RecursiveWithSymLink DISABLED_RecursiveWithSymLink
+#endif  // defined(OS_ANDROID)
 TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
   if (!FilePathWatcher::RecursiveWatchAvailable())
     return;
@@ -610,6 +639,14 @@
 }
 
 // Verify that changing attributes on a file is caught
+#if defined(OS_ANDROID)
+// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
+// "fuse" file system, while /data uses "ext4".  Running these tests in /data
+// would be preferable and allow testing file attributes and symlinks.
+// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
+// the |temp_dir_| in /data.
+#define FileAttributesChanged DISABLED_FileAttributesChanged
+#endif  // defined(OS_ANDROID
 TEST_F(FilePathWatcherTest, FileAttributesChanged) {
   ASSERT_TRUE(WriteFile(test_file(), "content"));
   FilePathWatcher watcher;
diff --git a/base/md5.cc b/base/md5.cc
index 064e28ac..eff5308 100644
--- a/base/md5.cc
+++ b/base/md5.cc
@@ -277,7 +277,7 @@
   ret.resize(32);
 
   for (int i = 0, j = 0; i < 16; i++, j += 2) {
-    int a = digest.a[i];
+    uint8_t a = digest.a[i];
     ret[j] = zEncode[(a >> 4) & 0xf];
     ret[j + 1] = zEncode[a & 0xf];
   }
diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h
index 219437e..5f94b4c 100644
--- a/base/memory/ref_counted.h
+++ b/base/memory/ref_counted.h
@@ -14,6 +14,7 @@
 #ifndef NDEBUG
 #include "base/logging.h"
 #endif
+#include "base/move.h"
 #include "base/threading/thread_collision_warner.h"
 #include "build/build_config.h"
 
@@ -264,6 +265,7 @@
 //
 template <class T>
 class scoped_refptr {
+  TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_refptr)
  public:
   typedef T element_type;
 
@@ -286,6 +288,11 @@
       AddRef(ptr_);
   }
 
+  template <typename U>
+  scoped_refptr(scoped_refptr<U>&& r) : ptr_(r.get()) {
+    r.ptr_ = nullptr;
+  }
+
   ~scoped_refptr() {
     if (ptr_)
       Release(ptr_);
@@ -323,6 +330,17 @@
     return *this = r.get();
   }
 
+  scoped_refptr<T>& operator=(scoped_refptr<T>&& r) {
+    scoped_refptr<T>(r.Pass()).swap(*this);
+    return *this;
+  }
+
+  template <typename U>
+  scoped_refptr<T>& operator=(scoped_refptr<U>&& r) {
+    scoped_refptr<T>(r.Pass()).swap(*this);
+    return *this;
+  }
+
   void swap(T** pp) {
     T* p = ptr_;
     ptr_ = *pp;
@@ -334,6 +352,8 @@
   }
 
  private:
+  template <typename U> friend class scoped_refptr;
+
   // Allow scoped_refptr<T> to be used in boolean expressions, but not
   // implicitly convertible to a real bool (which is dangerous).
   //
diff --git a/base/memory/ref_counted_unittest.cc b/base/memory/ref_counted_unittest.cc
index f75cd38..6f8e599 100644
--- a/base/memory/ref_counted_unittest.cc
+++ b/base/memory/ref_counted_unittest.cc
@@ -40,19 +40,71 @@
 
   static bool was_destroyed() { return was_destroyed_; }
 
-  void SelfDestruct() { self_ptr_ = NULL; }
+  static void reset_was_destroyed() { was_destroyed_ = false; }
+
+  scoped_refptr<ScopedRefPtrToSelf> self_ptr_;
 
  private:
   friend class base::RefCounted<ScopedRefPtrToSelf>;
   ~ScopedRefPtrToSelf() { was_destroyed_ = true; }
 
   static bool was_destroyed_;
-
-  scoped_refptr<ScopedRefPtrToSelf> self_ptr_;
 };
 
 bool ScopedRefPtrToSelf::was_destroyed_ = false;
 
+class ScopedRefPtrCountBase : public base::RefCounted<ScopedRefPtrCountBase> {
+ public:
+  ScopedRefPtrCountBase() { ++constructor_count_; }
+
+  static int constructor_count() { return constructor_count_; }
+
+  static int destructor_count() { return destructor_count_; }
+
+  static void reset_count() {
+    constructor_count_ = 0;
+    destructor_count_ = 0;
+  }
+
+ protected:
+  virtual ~ScopedRefPtrCountBase() { ++destructor_count_; }
+
+ private:
+  friend class base::RefCounted<ScopedRefPtrCountBase>;
+
+  static int constructor_count_;
+  static int destructor_count_;
+};
+
+int ScopedRefPtrCountBase::constructor_count_ = 0;
+int ScopedRefPtrCountBase::destructor_count_ = 0;
+
+class ScopedRefPtrCountDerived : public ScopedRefPtrCountBase {
+ public:
+  ScopedRefPtrCountDerived() { ++constructor_count_; }
+
+  static int constructor_count() { return constructor_count_; }
+
+  static int destructor_count() { return destructor_count_; }
+
+  static void reset_count() {
+    constructor_count_ = 0;
+    destructor_count_ = 0;
+  }
+
+ protected:
+  ~ScopedRefPtrCountDerived() override { ++destructor_count_; }
+
+ private:
+  friend class base::RefCounted<ScopedRefPtrCountDerived>;
+
+  static int constructor_count_;
+  static int destructor_count_;
+};
+
+int ScopedRefPtrCountDerived::constructor_count_ = 0;
+int ScopedRefPtrCountDerived::destructor_count_ = 0;
+
 }  // end namespace
 
 TEST(RefCountedUnitTest, TestSelfAssignment) {
@@ -66,10 +118,24 @@
   CheckDerivedMemberAccess check;
 }
 
-TEST(RefCountedUnitTest, ScopedRefPtrToSelf) {
+TEST(RefCountedUnitTest, ScopedRefPtrToSelfPointerAssignment) {
+  ScopedRefPtrToSelf::reset_was_destroyed();
+
   ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf();
   EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed());
-  check->SelfDestruct();
+  check->self_ptr_ = nullptr;
+  EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
+}
+
+TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) {
+  ScopedRefPtrToSelf::reset_was_destroyed();
+
+  ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf();
+  EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed());
+  // Releasing |check->self_ptr_| will delete |check|.
+  // The move assignment operator must assign |check->self_ptr_| first then
+  // release |check->self_ptr_|.
+  check->self_ptr_ = scoped_refptr<ScopedRefPtrToSelf>();
   EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
 }
 
@@ -113,3 +179,284 @@
   EXPECT_EQ(p1, p2);
   EXPECT_EQ(p2, p1);
 }
+
+TEST(RefCountedUnitTest, SelfMoveAssignment) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p(raw);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    p = p.Pass();
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+    EXPECT_EQ(raw, p.get());
+
+    // p goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignment1) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2;
+
+      p2 = p1.Pass();
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(nullptr, p1.get());
+      EXPECT_EQ(raw, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignment2) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1;
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2(raw);
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+      p1 = p2.Pass();
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(raw, p1.get());
+      EXPECT_EQ(nullptr, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentSameInstance1) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2(p1);
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+      p1 = p2.Pass();
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(raw, p1.get());
+      EXPECT_EQ(nullptr, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentSameInstance2) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2(p1);
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+      p2 = p1.Pass();
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(nullptr, p1.get());
+      EXPECT_EQ(raw, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentDifferentInstances) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw1);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase();
+      scoped_refptr<ScopedRefPtrCountBase> p2(raw2);
+      EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+      p1 = p2.Pass();
+      EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(raw2, p1.get());
+      EXPECT_EQ(nullptr, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveAssignmentDerived) {
+  ScopedRefPtrCountBase::reset_count();
+  ScopedRefPtrCountDerived::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw1);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountDerived::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+    {
+      ScopedRefPtrCountDerived *raw2 = new ScopedRefPtrCountDerived();
+      scoped_refptr<ScopedRefPtrCountDerived> p2(raw2);
+      EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+      p1 = p2.Pass();
+      EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+      EXPECT_EQ(raw2, p1.get());
+      EXPECT_EQ(nullptr, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveConstructor) {
+  ScopedRefPtrCountBase::reset_count();
+
+  {
+    ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase();
+    scoped_refptr<ScopedRefPtrCountBase> p1(raw);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass());
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(nullptr, p1.get());
+      EXPECT_EQ(raw, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+}
+
+TEST(RefCountedUnitTest, MoveConstructorDerived) {
+  ScopedRefPtrCountBase::reset_count();
+  ScopedRefPtrCountDerived::reset_count();
+
+  {
+    ScopedRefPtrCountDerived *raw1 = new ScopedRefPtrCountDerived();
+    scoped_refptr<ScopedRefPtrCountDerived> p1(raw1);
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+    EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+
+    {
+      scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass());
+      EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count());
+      EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+      EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count());
+      EXPECT_EQ(nullptr, p1.get());
+      EXPECT_EQ(raw1, p2.get());
+
+      // p2 goes out of scope.
+    }
+    EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+    EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+
+    // p1 goes out of scope.
+  }
+  EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count());
+  EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count());
+}
+
diff --git a/base/move.h b/base/move.h
index 06f3f323..91fd0e7 100644
--- a/base/move.h
+++ b/base/move.h
@@ -226,4 +226,9 @@
   typedef void MoveOnlyTypeForCPP03; \
  private:
 
+#define TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \
+ public: \
+  type&& Pass() WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \
+ private:
+
 #endif  // BASE_MOVE_H_
diff --git a/base/synchronization/cancellation_flag.cc b/base/synchronization/cancellation_flag.cc
index ad3b551..ca5c0a8 100644
--- a/base/synchronization/cancellation_flag.cc
+++ b/base/synchronization/cancellation_flag.cc
@@ -19,4 +19,8 @@
   return base::subtle::Acquire_Load(&flag_) != 0;
 }
 
+void CancellationFlag::UnsafeResetForTesting() {
+  base::subtle::Release_Store(&flag_, 0);
+}
+
 }  // namespace base
diff --git a/base/synchronization/cancellation_flag.h b/base/synchronization/cancellation_flag.h
index 51a4def1..0f0f08ee 100644
--- a/base/synchronization/cancellation_flag.h
+++ b/base/synchronization/cancellation_flag.h
@@ -29,6 +29,11 @@
   void Set();
   bool IsSet() const;  // Returns true iff the flag was set.
 
+  // For subtle reasons that may be different on different architectures,
+  // a different thread testing IsSet() may erroneously read 'true' after
+  // this method has been called.
+  void UnsafeResetForTesting();
+
  private:
   base::subtle::Atomic32 flag_;
 #if !defined(NDEBUG)
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 6872aff9..8baeb147 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -116,6 +116,8 @@
     "trace_event_analyzer.h",
     "trace_to_file.cc",
     "trace_to_file.h",
+    "user_action_tester.cc",
+    "user_action_tester.h",
     "values_test_util.cc",
     "values_test_util.h",
   ]
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 0b09004d..ac79d756d 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -150,7 +150,7 @@
     KillSpawnedTestProcesses();
 
     // The signal would normally kill the process, so exit now.
-    exit(1);
+    _exit(1);
   }
 
   void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
diff --git a/base/test/user_action_tester.cc b/base/test/user_action_tester.cc
new file mode 100644
index 0000000..3fdab12
--- /dev/null
+++ b/base/test/user_action_tester.cc
@@ -0,0 +1,35 @@
+// 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/test/user_action_tester.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+
+namespace base {
+
+UserActionTester::UserActionTester()
+    : action_callback_(
+          base::Bind(&UserActionTester::OnUserAction, base::Unretained(this))) {
+  base::AddActionCallback(action_callback_);
+}
+
+UserActionTester::~UserActionTester() {
+  base::RemoveActionCallback(action_callback_);
+}
+
+int UserActionTester::GetActionCount(const std::string& user_action) const {
+  UserActionCountMap::const_iterator iter = count_map_.find(user_action);
+  return iter == count_map_.end() ? 0 : iter->second;
+}
+
+void UserActionTester::ResetCounts() {
+  count_map_.clear();
+}
+
+void UserActionTester::OnUserAction(const std::string& user_action) {
+  ++(count_map_[user_action]);
+}
+
+}  // namespace base
diff --git a/base/test/user_action_tester.h b/base/test/user_action_tester.h
new file mode 100644
index 0000000..6b0efc5
--- /dev/null
+++ b/base/test/user_action_tester.h
@@ -0,0 +1,46 @@
+// 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_TEST_USER_ACTION_TESTER_H_
+#define BASE_TEST_USER_ACTION_TESTER_H_
+
+#include <map>
+#include <string>
+
+#include "base/metrics/user_metrics.h"
+
+namespace base {
+
+// This class observes and collects user action notifications that are sent
+// by the tests, so that they can be examined afterwards for correctness.
+// Note: This class is NOT thread-safe.
+class UserActionTester {
+ public:
+  UserActionTester();
+  ~UserActionTester();
+
+  // Returns the number of times the given |user_action| occurred.
+  int GetActionCount(const std::string& user_action) const;
+
+  // Resets all user action counts to 0.
+  void ResetCounts();
+
+ private:
+  typedef std::map<std::string, int> UserActionCountMap;
+
+  // The callback that is notified when a user actions occurs.
+  void OnUserAction(const std::string& user_action);
+
+  // A map that tracks the number of times a user action has occurred.
+  UserActionCountMap count_map_;
+
+  // The callback that is added to the global action callback list.
+  base::ActionCallback action_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserActionTester);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_USER_ACTION_TESTER_H_
diff --git a/base/test/user_action_tester_unittest.cc b/base/test/user_action_tester_unittest.cc
new file mode 100644
index 0000000..a51849f6
--- /dev/null
+++ b/base/test/user_action_tester_unittest.cc
@@ -0,0 +1,86 @@
+// 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/test/user_action_tester.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const char kUserAction1[] = "user.action.1";
+const char kUserAction2[] = "user.action.2";
+const char kUserAction3[] = "user.action.3";
+
+// Record an action and cause all ActionCallback observers to be notified.
+void RecordAction(const char user_action[]) {
+  base::RecordAction(base::UserMetricsAction(user_action));
+}
+
+}  // namespace
+
+// Verify user action counts are zero initially.
+TEST(UserActionTesterTest, GetActionCountWhenNoActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction1));
+}
+
+// Verify user action counts are tracked properly.
+TEST(UserActionTesterTest, GetActionCountWhenActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction2);
+  RecordAction(kUserAction2);
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+  EXPECT_EQ(2, user_action_tester.GetActionCount(kUserAction2));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction3));
+}
+
+// Verify no seg faults occur when resetting action counts when none have been
+// recorded.
+TEST(UserActionTesterTest, ResetCountsWhenNoActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+  user_action_tester.ResetCounts();
+}
+
+// Verify user action counts are set to zero on a ResetCounts.
+TEST(UserActionTesterTest, ResetCountsWhenActionsHaveBeenRecorded) {
+  UserActionTester user_action_tester;
+
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction1);
+  RecordAction(kUserAction2);
+  user_action_tester.ResetCounts();
+
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction1));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction2));
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction3));
+}
+
+// Verify the UserActionsTester is notified when base::RecordAction is called.
+TEST(UserActionTesterTest, VerifyUserActionTesterListensForUserActions) {
+  UserActionTester user_action_tester;
+
+  base::RecordAction(base::UserMetricsAction(kUserAction1));
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+}
+
+// Verify the UserActionsTester is notified when base::RecordComputedAction is
+// called.
+TEST(UserActionTesterTest,
+     VerifyUserActionTesterListensForComputedUserActions) {
+  UserActionTester user_action_tester;
+
+  base::RecordComputedAction(kUserAction1);
+
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction1));
+}
+
+}  // namespace base
diff --git a/build/android/pylib/base/base_test_runner.py b/build/android/pylib/base/base_test_runner.py
index 4e2eae70..1ca03383 100644
--- a/build/android/pylib/base/base_test_runner.py
+++ b/build/android/pylib/base/base_test_runner.py
@@ -26,12 +26,11 @@
 class BaseTestRunner(object):
   """Base class for running tests on a single device."""
 
-  def __init__(self, device_serial, tool, cleanup_test_files=False):
+  def __init__(self, device_serial, tool):
     """
       Args:
         device: Tests will run on the device of this ID.
         tool: Name of the Valgrind tool.
-        cleanup_test_files: Whether or not to cleanup test files on device.
     """
     self.device_serial = device_serial
     self.device = device_utils.DeviceUtils(device_serial)
@@ -45,7 +44,6 @@
     # starting it in TestServerThread.
     self.test_server_spawner_port = 0
     self.test_server_port = 0
-    self._cleanup_test_files = cleanup_test_files
 
   def _PushTestServerPortInfoToDevice(self):
     """Pushes the latest port information to device."""
@@ -77,8 +75,6 @@
   def TearDown(self):
     """Run once after all tests are run."""
     self.ShutdownHelperToolsForTestSuite()
-    if self._cleanup_test_files:
-      self.device.old_interface.RemovePushedFiles()
 
   def LaunchTestHttpServer(self, document_root, port=None,
                            extra_config_contents=None):
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index f7167ce..3ad767b 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -27,6 +27,7 @@
 from pylib import constants
 from pylib.device import adb_wrapper
 from pylib.device import decorators
+from pylib.device import device_blacklist
 from pylib.device import device_errors
 from pylib.device import intent
 from pylib.device import logcat_monitor
@@ -72,6 +73,7 @@
   },
 ]
 
+
 @decorators.WithExplicitTimeoutAndRetries(
     _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
 def GetAVDs():
@@ -1505,7 +1507,7 @@
     """Creates a Parallelizer to operate over the provided list of devices.
 
     If |devices| is either |None| or an empty list, the Parallelizer will
-    operate over all attached devices.
+    operate over all attached devices that have not been blacklisted.
 
     Args:
       devices: A list of either DeviceUtils instances or objects from
@@ -1518,9 +1520,12 @@
       A Parallelizer operating over |devices|.
     """
     if not devices:
-      devices = adb_wrapper.AdbWrapper.GetDevices()
+      blacklist = device_blacklist.ReadBlacklist()
+      devices = [d for d in adb_wrapper.AdbWrapper.GetDevices()
+                 if d.GetDeviceSerial() not in blacklist]
       if not devices:
         raise device_errors.NoDevicesError()
+
     devices = [d if isinstance(d, cls) else cls(d) for d in devices]
     if async:
       return parallelizer.Parallelizer(devices)
diff --git a/build/android/pylib/gtest/test_options.py b/build/android/pylib/gtest/test_options.py
index 5099ba6..58cd82b 100644
--- a/build/android/pylib/gtest/test_options.py
+++ b/build/android/pylib/gtest/test_options.py
@@ -8,7 +8,6 @@
 
 GTestOptions = collections.namedtuple('GTestOptions', [
     'tool',
-    'cleanup_test_files',
     'gtest_filter',
     'run_disabled',
     'test_arguments',
diff --git a/build/android/pylib/gtest/test_package_exe.py b/build/android/pylib/gtest/test_package_exe.py
index e607cad..ee95b5c 100644
--- a/build/android/pylib/gtest/test_package_exe.py
+++ b/build/android/pylib/gtest/test_package_exe.py
@@ -6,6 +6,7 @@
 
 import logging
 import os
+import posixpath
 import sys
 import tempfile
 
@@ -119,14 +120,19 @@
 
   #override
   def GetAllTests(self, device):
-    cmd = '%s %s/%s --gtest_list_tests' % (self.tool.GetTestWrapper(),
-        constants.TEST_EXECUTABLE_DIR, self.suite_name)
-    lib_path = '%s/%s_deps' % (constants.TEST_EXECUTABLE_DIR, self.suite_name)
-    (exit_code, output) = device.old_interface.GetAndroidToolStatusAndOutput(
-        cmd, lib_path=lib_path)
-    if exit_code != 0:
-      raise Exception(
-          'Failed to start binary:\n%s' % '\n'.join(output))
+    lib_path = posixpath.join(
+        constants.TEST_EXECUTABLE_DIR, '%s_deps' % self.suite_name)
+
+    cmd = []
+    for wrapper in (device.GetDevicePieWrapper(), self.tool.GetTestWrapper()):
+      if wrapper:
+        cmd.append(wrapper)
+    cmd.extend([
+        posixpath.join(constants.TEST_EXECUTABLE_DIR, self.suite_name),
+        '--gtest_list_tests'])
+
+    output = device.RunShellCommand(
+        cmd, check_return=True, env={'LD_LIBRARY_PATH': lib_path})
     return gtest_test_instance.ParseGTestListTests(output)
 
   #override
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py
index 4bb9737..0fc6c12 100644
--- a/build/android/pylib/gtest/test_runner.py
+++ b/build/android/pylib/gtest/test_runner.py
@@ -49,8 +49,7 @@
       test_package: An instance of TestPackage class.
     """
 
-    super(TestRunner, self).__init__(device, test_options.tool,
-                                     test_options.cleanup_test_files)
+    super(TestRunner, self).__init__(device, test_options.tool)
 
     self.test_package = test_package
     self.test_package.tool = self.tool
diff --git a/build/android/pylib/host_driven/setup.py b/build/android/pylib/host_driven/setup.py
index 285bcd40..b2ed348 100644
--- a/build/android/pylib/host_driven/setup.py
+++ b/build/android/pylib/host_driven/setup.py
@@ -195,7 +195,6 @@
   def TestRunnerFactory(device, shard_index):
     return test_runner.HostDrivenTestRunner(
         device, shard_index,
-        instrumentation_options.tool,
-        instrumentation_options.cleanup_test_files)
+        instrumentation_options.tool)
 
   return (TestRunnerFactory, available_tests)
diff --git a/build/android/pylib/host_driven/test_case.py b/build/android/pylib/host_driven/test_case.py
index 8c372cda..a7c6a18 100644
--- a/build/android/pylib/host_driven/test_case.py
+++ b/build/android/pylib/host_driven/test_case.py
@@ -50,8 +50,6 @@
       instrumentation_options: An InstrumentationOptions object.
     """
     class_name = self.__class__.__name__
-    self.adb = None
-    self.cleanup_test_files = False
     self.device = None
     self.device_id = ''
     self.has_forwarded_ports = False
@@ -67,15 +65,12 @@
 
   # TODO(bulach): make ports_to_forward not optional and move the Forwarder
   # mapping here.
-  def SetUp(self, device, shard_index,
-            cleanup_test_files, ports_to_forward=None):
+  def SetUp(self, device, shard_index, ports_to_forward=None):
     if not ports_to_forward:
       ports_to_forward = []
     self.device_id = device
     self.shard_index = shard_index
     self.device = device_utils.DeviceUtils(self.device_id)
-    self.adb = self.device.old_interface
-    self.cleanup_test_files = cleanup_test_files
     if ports_to_forward:
       self.ports_to_forward = ports_to_forward
 
diff --git a/build/android/pylib/host_driven/test_runner.py b/build/android/pylib/host_driven/test_runner.py
index 865be20..5e175bc 100644
--- a/build/android/pylib/host_driven/test_runner.py
+++ b/build/android/pylib/host_driven/test_runner.py
@@ -47,23 +47,27 @@
   result, rather than being re-raised on the main thread.
   """
 
+  # TODO(jbudorick): Remove cleanup_test_files once it's no longer used.
+  # pylint: disable=unused-argument
   #override
-  def __init__(self, device, shard_index, tool, cleanup_test_files):
+  def __init__(self, device, shard_index, tool, cleanup_test_files=None):
     """Creates a new HostDrivenTestRunner.
 
     Args:
       device: Attached android device.
       shard_index: Shard index.
       tool: Name of the Valgrind tool.
-      cleanup_test_files: Whether or not to cleanup test files on device.
+      cleanup_test_files: Deprecated.
     """
 
-    super(HostDrivenTestRunner, self).__init__(device, tool, cleanup_test_files)
+    super(HostDrivenTestRunner, self).__init__(device, tool)
 
     # The shard index affords the ability to create unique port numbers (e.g.
     # DEFAULT_PORT + shard_index) if the test so wishes.
     self.shard_index = shard_index
 
+  # pylint: enable=unused-argument
+
   #override
   def RunTest(self, test):
     """Sets up and runs a test case.
@@ -82,7 +86,7 @@
     exception_raised = False
 
     try:
-      test.SetUp(str(self.device), self.shard_index, self._cleanup_test_files)
+      test.SetUp(str(self.device), self.shard_index)
     except Exception:
       logging.exception(
           'Caught exception while trying to run SetUp() for test: ' +
diff --git a/build/android/pylib/instrumentation/test_options.py b/build/android/pylib/instrumentation/test_options.py
index 4a16b11..792010b8 100644
--- a/build/android/pylib/instrumentation/test_options.py
+++ b/build/android/pylib/instrumentation/test_options.py
@@ -8,7 +8,6 @@
 
 InstrumentationOptions = collections.namedtuple('InstrumentationOptions', [
     'tool',
-    'cleanup_test_files',
     'annotations',
     'exclude_annotations',
     'test_filter',
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index f3983fd9..adc424b0 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -48,8 +48,7 @@
       test_pkg: A TestPackage object.
       additional_flags: A list of additional flags to add to the command line.
     """
-    super(TestRunner, self).__init__(device, test_options.tool,
-                                     test_options.cleanup_test_files)
+    super(TestRunner, self).__init__(device, test_options.tool)
     self._lighttp_port = constants.LIGHTTPD_RANDOM_PORT_FIRST + shard_index
     self._logcat_monitor = None
 
diff --git a/build/android/pylib/linker/setup.py b/build/android/pylib/linker/setup.py
index ff21f10..5776f5af 100644
--- a/build/android/pylib/linker/setup.py
+++ b/build/android/pylib/linker/setup.py
@@ -40,7 +40,6 @@
                  if t.qualified_name in filtered_test_names]
 
   def TestRunnerFactory(device, _shard_index):
-    return test_runner.LinkerTestRunner(
-        device, args.tool, args.cleanup_test_files)
+    return test_runner.LinkerTestRunner(device, args.tool)
 
   return (TestRunnerFactory, all_tests)
diff --git a/build/android/pylib/linker/test_runner.py b/build/android/pylib/linker/test_runner.py
index 3680f83..b6803e4 100644
--- a/build/android/pylib/linker/test_runner.py
+++ b/build/android/pylib/linker/test_runner.py
@@ -49,16 +49,14 @@
   """
 
   #override
-  def __init__(self, device, tool, cleanup_test_files):
+  def __init__(self, device, tool):
     """Creates a new LinkerTestRunner.
 
     Args:
       device: Attached android device.
       tool: Name of the Valgrind tool.
-      cleanup_test_files: Whether or not to cleanup test files on device.
     """
-
-    super(LinkerTestRunner, self).__init__(device, tool, cleanup_test_files)
+    super(LinkerTestRunner, self).__init__(device, tool)
 
   #override
   def InstallTestPackage(self):
@@ -68,8 +66,7 @@
     if not os.path.exists(apk_path):
       raise Exception('%s not found, please build it' % apk_path)
 
-    package_name = apk_helper.GetPackageName(apk_path)
-    self.device.old_interface.ManagedInstall(apk_path, package_name)
+    self.device.Install(apk_path)
 
   #override
   def RunTest(self, test):
diff --git a/build/android/pylib/perf/perf_control.py b/build/android/pylib/perf/perf_control.py
index 97fa4a7..b6a0989e 100644
--- a/build/android/pylib/perf/perf_control.py
+++ b/build/android/pylib/perf/perf_control.py
@@ -28,7 +28,7 @@
 
   def SetHighPerfMode(self):
     """Sets the highest stable performance mode for the device."""
-    if not self._device.old_interface.IsRootEnabled():
+    if not self._device.HasRoot():
       message = 'Need root for performance mode. Results may be NOISY!!'
       logging.warning(message)
       # Add an additional warning at exit, such that it's clear that any results
@@ -58,13 +58,13 @@
     self._ForceAllCpusOnline(True)
     self._SetScalingGovernorInternal('performance')
     if not self._AllCpusAreOnline():
-      if not self._device.old_interface.IsRootEnabled():
+      if not self._device.HasRoot():
         raise RuntimeError('Need root to force CPUs online.')
       raise RuntimeError('Failed to force CPUs online.')
 
   def SetDefaultPerfMode(self):
     """Sets the performance mode for the device to its default mode."""
-    if not self._device.old_interface.IsRootEnabled():
+    if not self._device.HasRoot():
       return
     product_model = self._device.product_model
     if 'Nexus 5' == product_model:
diff --git a/build/android/pylib/perf/test_runner.py b/build/android/pylib/perf/test_runner.py
index da66d07..9d1f437 100644
--- a/build/android/pylib/perf/test_runner.py
+++ b/build/android/pylib/perf/test_runner.py
@@ -170,7 +170,7 @@
       tests: a dict mapping test_name to command.
       flaky_tests: a list of flaky test_name.
     """
-    super(TestRunner, self).__init__(device, None, 'Release')
+    super(TestRunner, self).__init__(device, None)
     self._options = test_options
     self._shard_index = shard_index
     self._max_shard = max_shard
diff --git a/build/android/pylib/screenshot.py b/build/android/pylib/screenshot.py
index ea939b7..2bbde037 100644
--- a/build/android/pylib/screenshot.py
+++ b/build/android/pylib/screenshot.py
@@ -6,6 +6,7 @@
 import os
 import signal
 import tempfile
+import time
 
 from pylib import cmd_helper
 from pylib.device import device_errors
@@ -87,10 +88,13 @@
     Returns:
       Output video file name on the host.
     """
-    host_file_name = host_file or ('screen-recording-%s.mp4' %
-                                   self._device.old_interface.GetTimestamp())
+    # TODO(jbudorick): Merge filename generation with the logic for doing so in
+    # DeviceUtils.
+    host_file_name = (
+        host_file
+        or 'screen-recording-%s.mp4' % time.strftime('%Y%m%dT%H%M%S',
+                                                     time.localtime()))
     host_file_name = os.path.abspath(host_file_name)
-    self._device.old_interface.EnsureHostDirectory(host_file_name)
     self._device.PullFile(self._device_file, host_file_name)
     self._device.RunShellCommand('rm -f "%s"' % self._device_file)
     return host_file_name
diff --git a/build/android/pylib/uiautomator/test_options.py b/build/android/pylib/uiautomator/test_options.py
index b383b201..3f5f950 100644
--- a/build/android/pylib/uiautomator/test_options.py
+++ b/build/android/pylib/uiautomator/test_options.py
@@ -8,7 +8,6 @@
 
 UIAutomatorOptions = collections.namedtuple('UIAutomatorOptions', [
     'tool',
-    'cleanup_test_files',
     'annotations',
     'exclude_annotations',
     'test_filter',
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py
index d7a4bdf..296bd47 100644
--- a/build/android/pylib/uiautomator/test_runner.py
+++ b/build/android/pylib/uiautomator/test_runner.py
@@ -26,7 +26,6 @@
     # Create an InstrumentationOptions object to pass to the super class
     instrumentation_options = instr_test_options.InstrumentationOptions(
         test_options.tool,
-        test_options.cleanup_test_files,
         test_options.annotations,
         test_options.exclude_annotations,
         test_options.test_filter,
diff --git a/build/android/pylib/utils/test_environment.py b/build/android/pylib/utils/test_environment.py
index 4d88a45..e78eb5c 100644
--- a/build/android/pylib/utils/test_environment.py
+++ b/build/android/pylib/utils/test_environment.py
@@ -34,13 +34,14 @@
   """Clean up the test environment, restarting fresh adb and HTTP daemons."""
   _KillWebServers()
   device_utils.RestartServer()
-  p = device_utils.DeviceUtils.parallel()
-  p.old_interface.RestartAdbdOnDevice()
-  try:
-    p.EnableRoot()
-  except device_errors.CommandFailedError as e:
-    # TODO(jbudorick) Handle this exception appropriately after interface
-    #                 conversions are finished.
-    logging.error(str(e))
-  p.WaitUntilFullyBooted()
+
+  def cleanup_device(d):
+    d.old_interface.RestartAdbdOnDevice()
+    try:
+      d.EnableRoot()
+    except device_errors.CommandFailedError as e:
+      logging.error(str(e))
+    d.WaitUntilFullyBooted()
+
+  device_utils.DeviceUtils.parallel().pMap(cleanup_device)
 
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index b1bd6e7..23d46b71 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -175,9 +175,6 @@
 def AddDeviceOptions(parser):
   """Adds device options to |parser|."""
   group = parser.add_argument_group(title='Device Options')
-  group.add_argument('-c', dest='cleanup_test_files',
-                     help='Cleanup test files on the device after run',
-                     action='store_true')
   group.add_argument('--tool',
                      dest='tool',
                      help=('Run the test under a tool '
@@ -372,7 +369,6 @@
   # TODO(jbudorick): Get rid of InstrumentationOptions.
   return instrumentation_test_options.InstrumentationOptions(
       args.tool,
-      args.cleanup_test_files,
       args.annotations,
       args.exclude_annotations,
       args.test_filter,
@@ -437,7 +433,6 @@
 
   return uiautomator_test_options.UIAutomatorOptions(
       args.tool,
-      args.cleanup_test_files,
       args.annotations,
       args.exclude_annotations,
       args.test_filter,
@@ -636,7 +631,6 @@
     # into the gtest code.
     gtest_options = gtest_test_options.GTestOptions(
         args.tool,
-        args.cleanup_test_files,
         args.test_filter,
         args.run_disabled,
         args.test_arguments,
diff --git a/build/android/update_verification.py b/build/android/update_verification.py
index fe89567..9539d94 100755
--- a/build/android/update_verification.py
+++ b/build/android/update_verification.py
@@ -17,7 +17,7 @@
 
 def _SaveAppData(device, package_name, from_apk=None, data_dir=None):
   def _BackupAppData(data_dir=None):
-    device.old_interface.Adb().SendCommand('backup %s' % package_name)
+    device.adb.Backup(package_name)
     backup_file = os.path.join(os.getcwd(), 'backup.ab')
     assert os.path.exists(backup_file), 'Backup failed.'
     if data_dir:
@@ -29,8 +29,7 @@
 
   if from_apk:
     logging.info('Installing %s...', from_apk)
-    # TODO(jbudorick) Switch to AdbWrapper.Install on the impl switch.
-    output = device.old_interface.Install(from_apk, reinstall=True)
+    output = device.Install(from_apk, reinstall=True)
     if 'Success' not in output:
       raise Exception('Unable to install %s. output: %s' % (from_apk, output))
 
@@ -42,14 +41,13 @@
 def _VerifyAppUpdate(device, to_apk, app_data, from_apk=None):
   def _RestoreAppData():
     assert os.path.exists(app_data), 'Backup file does not exist!'
-    device.old_interface.Adb().SendCommand('restore %s' % app_data)
+    device.adb.Restore(app_data)
     # It seems restore command is not synchronous.
     time.sleep(15)
 
   if from_apk:
     logging.info('Installing %s...', from_apk)
-    # TODO(jbudorick) Switch to AdbWrapper.Install on the impl switch.
-    output = device.old_interface.Install(from_apk, reinstall=True)
+    output = device.Install(from_apk, reinstall=True)
     if 'Success' not in output:
       raise Exception('Unable to install %s. output: %s' % (from_apk, output))
 
@@ -59,8 +57,7 @@
 
   logging.info('Verifying that %s cannot be installed side-by-side...',
                to_apk)
-  # TODO(jbudorick) Switch to AdbWrapper.Install on the impl switch.
-  output = device.old_interface.Install(to_apk)
+  output = device.Install(to_apk)
   if 'INSTALL_FAILED_ALREADY_EXISTS' not in output:
     if 'Success' in output:
       raise Exception('Package name has changed! output: %s' % output)
@@ -68,8 +65,7 @@
       raise Exception(output)
 
   logging.info('Verifying that %s can be overinstalled...', to_apk)
-  # TODO(jbudorick) Switch to AdbWrapper.Install on the impl switch.
-  output = device.old_interface.Install(to_apk, reinstall=True)
+  output = device.adb.Install(to_apk, reinstall=True)
   if 'Success' not in output:
     raise Exception('Unable to install %s.\n output: %s' % (to_apk, output))
   logging.info('Successfully updated to the new apk. Please verify that the '
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 22310d4..8185b03f 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -733,7 +733,7 @@
 #   datadeps, testonly
 #
 # Example
-#   java_library("foo") {
+#   java_binary("foo") {
 #     java_files = [ "org/chromium/foo/FooMain.java" ]
 #     deps = [ ":bar_java" ]
 #     main_class = "org.chromium.foo.FooMain"
@@ -834,7 +834,7 @@
   }
 }
 
-# Declare an java library target
+# Declare a java library target
 #
 # Variables
 #   deps: Specifies the dependencies of this target. Java targets in this list
@@ -932,7 +932,7 @@
   }
 }
 
-# Declare an java library target for a prebuilt jar
+# Declare a java library target for a prebuilt jar
 #
 # Variables
 #   deps: Specifies the dependencies of this target. Java targets in this list
diff --git a/build/gn_migration.gypi b/build/gn_migration.gypi
index 0da18bdb..1f9f178 100644
--- a/build/gn_migration.gypi
+++ b/build/gn_migration.gypi
@@ -254,6 +254,12 @@
              '../remoting/remoting.gyp:remoting_key_tester',
           ],
         }],
+        ['toolkit_views==1', {
+          'dependencies': [
+            '../ui/app_list/app_list.gyp:app_list_demo',
+            '../ui/views/views.gyp:views_unittests',
+          ],
+        }],
         ['use_x11==1', {
           'dependencies': [
             '../media/media.gyp:player_x11',
@@ -267,12 +273,6 @@
             }],
           ],
         }],
-        ['toolkit_views==1', {
-          'dependencies': [
-            '../ui/app_list/app_list.gyp:app_list_demo',
-            '../ui/views/views.gyp:views_unittests',
-          ],
-        }],
         ['use_ash==1', {
           'dependencies': [
             '../ash/ash.gyp:ash_shell',
@@ -295,7 +295,9 @@
         }],
         ['OS=="win" or OS=="mac" or chromeos==1', {
           'dependencies': [
+            '../rlz/rlz.gyp:rlz_id',
             '../rlz/rlz.gyp:rlz_lib',
+            '../rlz/rlz.gyp:rlz_unittests',
           ],
         }],
         ['OS=="android"', {
@@ -364,6 +366,11 @@
             '../url/url.gyp:url_unittests',
           ],
         }],
+        ['OS=="android" or OS=="linux"', {
+          'dependencies': [
+            '../net/net.gyp:disk_cache_memory_test',
+          ],
+        }],
         ['OS=="linux"', {
           'dependencies': [
             '../breakpad/breakpad.gyp:breakpad_unittests',
@@ -443,7 +450,21 @@
         }],
         ['OS=="win"', {
           'dependencies': [
+            '../base/base.gyp:pe_image_test',
+            '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests',
+            '../chrome_elf/chrome_elf.gyp:dll_hash_main',
+            '../components/components.gyp:wifi_test',
+            '../net/net.gyp:quic_client',
+            '../net/net.gyp:quic_server',
+            '../sandbox/sandbox.gyp:pocdll',
+            '../sandbox/sandbox.gyp:sandbox_poc',
+            '../sandbox/sandbox.gyp:sbox_integration_tests',
+            '../sandbox/sandbox.gyp:sbox_unittests',
+            '../sandbox/sandbox.gyp:sbox_validation_tests',
+            '../testing/gtest.gyp:gtest_main',
             '../third_party/codesighs/codesighs.gyp:msdump2symdb',
+            '../third_party/codesighs/codesighs.gyp:msmap2tsv',
+            '../third_party/pdfium/samples/samples.gyp:pdfium_diff',
             '../win8/win8.gyp:metro_viewer',
           ],
         }, {
@@ -451,18 +472,13 @@
             '../third_party/codesighs/codesighs.gyp:nm2tsv',
           ],
         }],
-        ['OS=="android" or OS=="linux"', {
-          'dependencies': [
-            '../net/net.gyp:disk_cache_memory_test',
-          ],
-        }],
       ],
     },
     {
       'target_name': 'gyp_only',
       'type': 'none',
       'conditions': [
-        ['OS=="linux"', {
+        ['OS=="linux" or OS=="win"', {
           'conditions': [
             ['disable_nacl==0 and disable_nacl_untrusted==0', {
               'dependencies': [
@@ -529,6 +545,7 @@
         }],
         ['use_openssl==1', {
           'dependencies': [
+            # TODO(GYP): All of these targets still need to be converted.
             '../third_party/boringssl/boringssl_tests.gyp:boringssl_ecdsa_test',
             '../third_party/boringssl/boringssl_tests.gyp:boringssl_bn_test',
             '../third_party/boringssl/boringssl_tests.gyp:boringssl_pqueue_test',
@@ -558,16 +575,17 @@
             '../third_party/boringssl/boringssl_tests.gyp:boringssl_unittests',
           ],
         }],
-        ['OS=="linux"', {
+        ['OS=="linux" or OS=="win"', {
           'dependencies': [
+            # TODO(GYP): in progress - see tfarina.
             '../third_party/webrtc/tools/tools.gyp:frame_analyzer',
             '../third_party/webrtc/tools/tools.gyp:rgba_to_i420_converter',
           ],
         }],
         ['OS=="win"', {
           'dependencies': [
+            # TODO(GYP): All of these targets still need to be converted.
             '../base/base.gyp:debug_message',
-            '../base/base.gyp:pe_image_test',
             '../chrome/chrome.gyp:app_installer',
             '../chrome/chrome.gyp:app_installer_unittests',
             '../chrome/chrome.gyp:app_shim',
@@ -581,11 +599,6 @@
             '../chrome/chrome.gyp:setup_unittests',
             '../chrome/installer/mini_installer.gyp:mini_installer',
             '../chrome/tools/crash_service/caps/caps.gyp:caps',
-            '../chrome_elf/chrome_elf.gyp:blacklist_test_dll_2',
-            '../chrome_elf/chrome_elf.gyp:blacklist_test_dll_3',
-            '../chrome_elf/chrome_elf.gyp:blacklist_test_main_dll',
-            '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests',
-            '../chrome_elf/chrome_elf.gyp:dll_hash_main',
             '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device',
             '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device_unittests',
             '../cloud_print/service/service.gyp:cloud_print_service',
@@ -593,33 +606,18 @@
             '../cloud_print/service/service.gyp:cloud_print_service_setup',
             '../cloud_print/virtual_driver/win/install/virtual_driver_install.gyp:virtual_driver_setup',
             '../cloud_print/virtual_driver/win/virtual_driver.gyp:gcp_portmon',
-            '../components/components.gyp:wifi_test',
             '../content/content_shell_and_tests.gyp:content_shell_crash_service',
             '../content/content_shell_and_tests.gyp:layout_test_helper',
             '../content/content_shell_and_tests.gyp:video_decode_accelerator_unittest',
             '../gpu/gpu.gyp:angle_end2end_tests',
             '../gpu/gpu.gyp:angle_perftests',
             '../net/net.gyp:net_docs',
-            '../net/net.gyp:quic_client',
-            '../net/net.gyp:quic_server',
             '../ppapi/ppapi_internal.gyp:ppapi_perftests',
             '../remoting/app_remoting_test.gyp:ar_sample_test_driver',
             '../remoting/remoting.gyp:remoting_breakpad_tester',
             '../remoting/remoting.gyp:remoting_console',
             '../remoting/remoting.gyp:remoting_desktop',
             '../rlz/rlz.gyp:rlz',
-            '../rlz/rlz.gyp:rlz_id',
-            '../rlz/rlz.gyp:rlz_unittests',
-            '../sandbox/sandbox.gyp:pocdll',
-            '../sandbox/sandbox.gyp:sandbox_poc',
-            '../sandbox/sandbox.gyp:sbox_integration_tests',
-            '../sandbox/sandbox.gyp:sbox_unittests',
-            '../sandbox/sandbox.gyp:sbox_validation_tests',
-            '../testing/gtest.gyp:gtest_main',
-            '../third_party/codesighs/codesighs.gyp:msmap2tsv',
-            '../third_party/pdfium/samples/samples.gyp:pdfium_diff',
-            '../third_party/webrtc/tools/tools.gyp:frame_analyzer',
-            '../third_party/webrtc/tools/tools.gyp:rgba_to_i420_converter',
             '../tools/win/static_initializers/static_initializers.gyp:static_initializers',
           ],
         }],
@@ -630,6 +628,7 @@
         }],
         ['OS=="win" and target_arch=="ia32"', {
           'dependencies': [
+            # TODO(GYP): All of these targets need to be ported over.
             '../base/base.gyp:base_win64',
             '../base/base.gyp:base_i18n_nacl_win64',
             '../chrome/chrome.gyp:crash_service_win64',
@@ -645,6 +644,7 @@
         }],
         ['OS=="win" and target_arch=="ia32" and configuration_policy==1', {
           'dependencies': [
+            # TODO(GYP): All of these targets need to be ported over.
             '../components/components.gyp:policy_win64',
           ]
         }],
@@ -667,6 +667,17 @@
         'chromium_gpu_builder',
         'chromium_gpu_debug_builder',
       ],
+      'conditions': [
+        ['OS=="win"', {
+          'dependencies': [
+            'chromium_builder',
+            'chromium_builder_dbg_drmemory_win',
+            'chromium_builder_nacl_sdk',
+            'chromium_builder_lkgr_drmemory_win',
+            'chromium_builder_dbg_tsan_win',
+          ],
+        }],
+      ],
     },
   ]
 }
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index b33b2ea..b724a99 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -100,7 +100,7 @@
 IDR_TOOLBAR_SHADOW_FULL_BLEED
 IDR_TRANSLATE_JS
 IDR_UBER_UTILS_JS
-IDR_WEBUI_I18N_TEMPLATE2_JS
+IDR_WEBUI_I18N_TEMPLATE_JS
 IDR_WEBUI_I18N_TEMPLATE_POLYMER_JS
 IDR_WEBUI_JSTEMPLATE_JS
 IDR_WEBUI_JS_LOAD_TIME_DATA
diff --git a/cc/base/unique_notifier_unittest.cc b/cc/base/unique_notifier_unittest.cc
index dfab85e6..7b52512 100644
--- a/cc/base/unique_notifier_unittest.cc
+++ b/cc/base/unique_notifier_unittest.cc
@@ -4,9 +4,9 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/message_loop/message_loop_proxy.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/base/unique_notifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -32,7 +32,7 @@
 TEST_F(UniqueNotifierTest, Schedule) {
   {
     UniqueNotifier notifier(
-        base::MessageLoopProxy::current().get(),
+        base::ThreadTaskRunnerHandle::Get().get(),
         base::Bind(&UniqueNotifierTest::Notify, base::Unretained(this)));
 
     EXPECT_EQ(0, NotificationCount());
diff --git a/cc/debug/micro_benchmark.cc b/cc/debug/micro_benchmark.cc
index 64629de..cf6e2698 100644
--- a/cc/debug/micro_benchmark.cc
+++ b/cc/debug/micro_benchmark.cc
@@ -7,7 +7,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
 #include "base/values.h"
 #include "cc/debug/micro_benchmark_impl.h"
 
@@ -46,14 +46,14 @@
 }
 
 scoped_ptr<MicroBenchmarkImpl> MicroBenchmark::GetBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop) {
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) {
   DCHECK(!processed_for_benchmark_impl_);
   processed_for_benchmark_impl_ = true;
-  return CreateBenchmarkImpl(origin_loop);
+  return CreateBenchmarkImpl(origin_task_runner);
 }
 
 scoped_ptr<MicroBenchmarkImpl> MicroBenchmark::CreateBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop) {
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) {
   return make_scoped_ptr<MicroBenchmarkImpl>(nullptr);
 }
 
diff --git a/cc/debug/micro_benchmark.h b/cc/debug/micro_benchmark.h
index 1654c36..c5bbd88 100644
--- a/cc/debug/micro_benchmark.h
+++ b/cc/debug/micro_benchmark.h
@@ -10,8 +10,8 @@
 #include "cc/base/cc_export.h"
 
 namespace base {
+class SingleThreadTaskRunner;
 class Value;
-class MessageLoopProxy;
 }  // namespace base
 
 namespace cc {
@@ -39,13 +39,13 @@
 
   bool ProcessedForBenchmarkImpl() const;
   scoped_ptr<MicroBenchmarkImpl> GetBenchmarkImpl(
-      scoped_refptr<base::MessageLoopProxy> origin_loop);
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner);
 
  protected:
   void NotifyDone(scoped_ptr<base::Value> result);
 
   virtual scoped_ptr<MicroBenchmarkImpl> CreateBenchmarkImpl(
-      scoped_refptr<base::MessageLoopProxy> origin_loop);
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner);
 
  private:
   DoneCallback callback_;
diff --git a/cc/debug/micro_benchmark_controller.cc b/cc/debug/micro_benchmark_controller.cc
index bf98100..a38e5ad 100644
--- a/cc/debug/micro_benchmark_controller.cc
+++ b/cc/debug/micro_benchmark_controller.cc
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/callback.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "cc/debug/invalidation_benchmark.h"
 #include "cc/debug/picture_record_benchmark.h"
@@ -54,7 +54,9 @@
 
 MicroBenchmarkController::MicroBenchmarkController(LayerTreeHost* host)
     : host_(host),
-      main_controller_message_loop_(base::MessageLoopProxy::current().get()) {
+      main_controller_task_runner_(base::ThreadTaskRunnerHandle::IsSet()
+                                       ? base::ThreadTaskRunnerHandle::Get()
+                                       : nullptr) {
   DCHECK(host_);
 }
 
@@ -102,8 +104,7 @@
        ++it) {
     scoped_ptr<MicroBenchmarkImpl> benchmark_impl;
     if (!(*it)->ProcessedForBenchmarkImpl()) {
-      benchmark_impl =
-          (*it)->GetBenchmarkImpl(main_controller_message_loop_);
+      benchmark_impl = (*it)->GetBenchmarkImpl(main_controller_task_runner_);
     }
 
     if (benchmark_impl.get())
diff --git a/cc/debug/micro_benchmark_controller.h b/cc/debug/micro_benchmark_controller.h
index 88f6d9ad..198e203f 100644
--- a/cc/debug/micro_benchmark_controller.h
+++ b/cc/debug/micro_benchmark_controller.h
@@ -13,8 +13,8 @@
 #include "cc/debug/micro_benchmark.h"
 
 namespace base {
+class SingleThreadTaskRunner;
 class Value;
-class MessageLoopProxy;
 }  // namespace base
 
 namespace cc {
@@ -44,7 +44,7 @@
   LayerTreeHost* host_;
   ScopedPtrVector<MicroBenchmark> benchmarks_;
   static int next_id_;
-  scoped_refptr<base::MessageLoopProxy> main_controller_message_loop_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_controller_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(MicroBenchmarkController);
 };
diff --git a/cc/debug/micro_benchmark_impl.cc b/cc/debug/micro_benchmark_impl.cc
index 7ec58c8..f20f32e 100644
--- a/cc/debug/micro_benchmark_impl.cc
+++ b/cc/debug/micro_benchmark_impl.cc
@@ -6,8 +6,9 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/location.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
 #include "base/values.h"
 
 namespace cc {
@@ -23,8 +24,11 @@
 
 MicroBenchmarkImpl::MicroBenchmarkImpl(
     const DoneCallback& callback,
-    scoped_refptr<base::MessageLoopProxy> origin_loop)
-    : callback_(callback), is_done_(false), origin_loop_(origin_loop) {}
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner)
+    : callback_(callback),
+      is_done_(false),
+      origin_task_runner_(origin_task_runner) {
+}
 
 MicroBenchmarkImpl::~MicroBenchmarkImpl() {}
 
@@ -35,9 +39,8 @@
 void MicroBenchmarkImpl::DidCompleteCommit(LayerTreeHostImpl* host) {}
 
 void MicroBenchmarkImpl::NotifyDone(scoped_ptr<base::Value> result) {
-  origin_loop_->PostTask(
-    FROM_HERE,
-    base::Bind(RunCallback, callback_, base::Passed(&result)));
+  origin_task_runner_->PostTask(
+      FROM_HERE, base::Bind(RunCallback, callback_, base::Passed(&result)));
   is_done_ = true;
 }
 
diff --git a/cc/debug/micro_benchmark_impl.h b/cc/debug/micro_benchmark_impl.h
index 4f3f74f..ed968a39 100644
--- a/cc/debug/micro_benchmark_impl.h
+++ b/cc/debug/micro_benchmark_impl.h
@@ -10,8 +10,8 @@
 #include "cc/base/cc_export.h"
 
 namespace base {
+class SingleThreadTaskRunner;
 class Value;
-class MessageLoopProxy;
 }  // namespace base
 
 namespace cc {
@@ -25,7 +25,7 @@
 
   explicit MicroBenchmarkImpl(
       const DoneCallback& callback,
-      scoped_refptr<base::MessageLoopProxy> origin_loop);
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner);
   virtual ~MicroBenchmarkImpl();
 
   bool IsDone() const;
@@ -40,7 +40,7 @@
  private:
   DoneCallback callback_;
   bool is_done_;
-  scoped_refptr<base::MessageLoopProxy> origin_loop_;
+  scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
 };
 
 }  // namespace cc
diff --git a/cc/debug/rasterize_and_record_benchmark.cc b/cc/debug/rasterize_and_record_benchmark.cc
index 54f48f3b8..ab05f30 100644
--- a/cc/debug/rasterize_and_record_benchmark.cc
+++ b/cc/debug/rasterize_and_record_benchmark.cc
@@ -94,10 +94,9 @@
 }
 
 scoped_ptr<MicroBenchmarkImpl> RasterizeAndRecordBenchmark::CreateBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop) {
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) {
   return make_scoped_ptr(new RasterizeAndRecordBenchmarkImpl(
-      origin_loop,
-      settings_.get(),
+      origin_task_runner, settings_.get(),
       base::Bind(&RasterizeAndRecordBenchmark::RecordRasterResults,
                  weak_ptr_factory_.GetWeakPtr())));
 }
diff --git a/cc/debug/rasterize_and_record_benchmark.h b/cc/debug/rasterize_and_record_benchmark.h
index 46f106fb..9215f2df 100644
--- a/cc/debug/rasterize_and_record_benchmark.h
+++ b/cc/debug/rasterize_and_record_benchmark.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "cc/debug/micro_benchmark_controller.h"
 #include "cc/resources/picture.h"
@@ -34,7 +35,7 @@
   void RunOnLayer(PictureLayer* layer) override;
 
   scoped_ptr<MicroBenchmarkImpl> CreateBenchmarkImpl(
-      scoped_refptr<base::MessageLoopProxy> origin_loop) override;
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) override;
 
  private:
   void RunOnDisplayListLayer(PictureLayer* layer,
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.cc b/cc/debug/rasterize_and_record_benchmark_impl.cc
index 443db8c..3049103 100644
--- a/cc/debug/rasterize_and_record_benchmark_impl.cc
+++ b/cc/debug/rasterize_and_record_benchmark_impl.cc
@@ -113,10 +113,10 @@
 }  // namespace
 
 RasterizeAndRecordBenchmarkImpl::RasterizeAndRecordBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop,
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner,
     base::Value* value,
     const MicroBenchmarkImpl::DoneCallback& callback)
-    : MicroBenchmarkImpl(callback, origin_loop),
+    : MicroBenchmarkImpl(callback, origin_task_runner),
       rasterize_repeat_count_(kDefaultRasterizeRepeatCount) {
   base::DictionaryValue* settings = nullptr;
   value->GetAsDictionary(&settings);
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.h b/cc/debug/rasterize_and_record_benchmark_impl.h
index ae134ab2..06224da 100644
--- a/cc/debug/rasterize_and_record_benchmark_impl.h
+++ b/cc/debug/rasterize_and_record_benchmark_impl.h
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "cc/debug/micro_benchmark_impl.h"
 #include "cc/resources/task_graph_runner.h"
@@ -21,7 +22,7 @@
 class RasterizeAndRecordBenchmarkImpl : public MicroBenchmarkImpl {
  public:
   explicit RasterizeAndRecordBenchmarkImpl(
-      scoped_refptr<base::MessageLoopProxy> origin_loop,
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner,
       base::Value* value,
       const MicroBenchmarkImpl::DoneCallback& callback);
   ~RasterizeAndRecordBenchmarkImpl() override;
diff --git a/cc/debug/unittest_only_benchmark.cc b/cc/debug/unittest_only_benchmark.cc
index 2bd5be9..81158122 100644
--- a/cc/debug/unittest_only_benchmark.cc
+++ b/cc/debug/unittest_only_benchmark.cc
@@ -5,7 +5,7 @@
 #include "cc/debug/unittest_only_benchmark.h"
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
 #include "base/values.h"
 #include "cc/debug/unittest_only_benchmark_impl.h"
 
@@ -53,13 +53,12 @@
 }
 
 scoped_ptr<MicroBenchmarkImpl> UnittestOnlyBenchmark::CreateBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop) {
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) {
   if (!create_impl_benchmark_)
     return make_scoped_ptr<MicroBenchmarkImpl>(nullptr);
 
   return make_scoped_ptr(new UnittestOnlyBenchmarkImpl(
-      origin_loop,
-      nullptr,
+      origin_task_runner, nullptr,
       base::Bind(&UnittestOnlyBenchmark::RecordImplResults,
                  weak_ptr_factory_.GetWeakPtr())));
 }
diff --git a/cc/debug/unittest_only_benchmark.h b/cc/debug/unittest_only_benchmark.h
index 7fa2b4dc..4e74ec7 100644
--- a/cc/debug/unittest_only_benchmark.h
+++ b/cc/debug/unittest_only_benchmark.h
@@ -8,6 +8,10 @@
 #include "base/memory/weak_ptr.h"
 #include "cc/debug/micro_benchmark.h"
 
+namespace base {
+class SingleThreadIdleTaskRunner;
+}
+
 namespace cc {
 
 class CC_EXPORT UnittestOnlyBenchmark : public MicroBenchmark {
@@ -21,7 +25,7 @@
 
  protected:
   scoped_ptr<MicroBenchmarkImpl> CreateBenchmarkImpl(
-      scoped_refptr<base::MessageLoopProxy> origin_loop) override;
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner) override;
 
  private:
   void RecordImplResults(scoped_ptr<base::Value> results);
diff --git a/cc/debug/unittest_only_benchmark_impl.cc b/cc/debug/unittest_only_benchmark_impl.cc
index a3b040f..59e3d27 100644
--- a/cc/debug/unittest_only_benchmark_impl.cc
+++ b/cc/debug/unittest_only_benchmark_impl.cc
@@ -4,16 +4,17 @@
 
 #include "cc/debug/unittest_only_benchmark_impl.h"
 
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
 #include "base/values.h"
 
 namespace cc {
 
 UnittestOnlyBenchmarkImpl::UnittestOnlyBenchmarkImpl(
-    scoped_refptr<base::MessageLoopProxy> origin_loop,
+    scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner,
     base::Value* settings,
     const DoneCallback& callback)
-    : MicroBenchmarkImpl(callback, origin_loop) {}
+    : MicroBenchmarkImpl(callback, origin_task_runner) {
+}
 
 UnittestOnlyBenchmarkImpl::~UnittestOnlyBenchmarkImpl() {}
 
diff --git a/cc/debug/unittest_only_benchmark_impl.h b/cc/debug/unittest_only_benchmark_impl.h
index f9b256d..1444850 100644
--- a/cc/debug/unittest_only_benchmark_impl.h
+++ b/cc/debug/unittest_only_benchmark_impl.h
@@ -9,8 +9,8 @@
 #include "cc/debug/micro_benchmark_impl.h"
 
 namespace base {
+class SingleThreadTaskRunner;
 class Value;
-class MessageLoopProxy;
 }
 
 namespace cc {
@@ -18,9 +18,10 @@
 class LayerTreeHostImpl;
 class CC_EXPORT UnittestOnlyBenchmarkImpl : public MicroBenchmarkImpl {
  public:
-  UnittestOnlyBenchmarkImpl(scoped_refptr<base::MessageLoopProxy> origin_loop,
-                            base::Value* settings,
-                            const DoneCallback& callback);
+  UnittestOnlyBenchmarkImpl(
+      scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner,
+      base::Value* settings,
+      const DoneCallback& callback);
   ~UnittestOnlyBenchmarkImpl() override;
 
   void DidCompleteCommit(LayerTreeHostImpl* host) override;
diff --git a/cc/layers/delegated_frame_resource_collection_unittest.cc b/cc/layers/delegated_frame_resource_collection_unittest.cc
index f91fe63..e5426a0 100644
--- a/cc/layers/delegated_frame_resource_collection_unittest.cc
+++ b/cc/layers/delegated_frame_resource_collection_unittest.cc
@@ -3,8 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/location.h"
 #include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "cc/layers/delegated_frame_resource_collection.h"
 #include "cc/resources/returned_resource.h"
@@ -95,7 +98,7 @@
   base::Thread thread("test thread");
   thread.Start();
   scoped_ptr<BlockingTaskRunner> main_thread_task_runner(
-      BlockingTaskRunner::Create(base::MessageLoopProxy::current()));
+      BlockingTaskRunner::Create(base::ThreadTaskRunnerHandle::Get()));
 
   TransferableResourceArray resources = CreateResourceArray();
   resource_collection_->ReceivedResources(resources);
@@ -110,14 +113,12 @@
     base::RunLoop run_loop;
     resources_available_closure_ = run_loop.QuitClosure();
 
-    thread.message_loop()->PostTask(
+    thread.message_loop()->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(
             &ReturnResourcesOnThread,
             resource_collection_->GetReturnResourcesCallbackForImplThread(),
-            returned_resources,
-            &event,
-            main_thread_task_runner.get()));
+            returned_resources, &event, main_thread_task_runner.get()));
 
     run_loop.Run();
   }
@@ -152,12 +153,10 @@
   returned_resources_.clear();
 
   base::WaitableEvent* null_event = nullptr;
-  thread.message_loop()->PostTask(FROM_HERE,
-                                  base::Bind(&ReturnResourcesOnThread,
-                                             return_callback,
-                                             returned_resources,
-                                             null_event,
-                                             main_thread_task_runner.get()));
+  thread.message_loop()->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&ReturnResourcesOnThread, return_callback, returned_resources,
+                 null_event, main_thread_task_runner.get()));
 
   thread.Stop();
 }
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc
index 72b2e134..c7cac01f 100644
--- a/cc/layers/layer_perftest.cc
+++ b/cc/layers/layer_perftest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/layer.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/debug/lap_timer.h"
 #include "cc/resources/layer_painter.h"
 #include "cc/test/fake_impl_proxy.h"
@@ -40,7 +41,7 @@
   void SetUp() override {
     layer_tree_host_ = FakeLayerTreeHost::Create(&fake_client_);
     layer_tree_host_->InitializeSingleThreaded(
-        &fake_client_, base::MessageLoopProxy::current(), nullptr);
+        &fake_client_, base::ThreadTaskRunnerHandle::Get(), nullptr);
   }
 
   void TearDown() override {
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 973c976..bae81850 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/layer.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/animation/keyframed_animation_curve.h"
 #include "cc/base/math_util.h"
 #include "cc/layers/layer_impl.h"
@@ -45,8 +46,7 @@
  public:
   explicit MockLayerTreeHost(FakeLayerTreeHostClient* client)
       : LayerTreeHost(client, nullptr, nullptr, nullptr, LayerTreeSettings()) {
-    InitializeSingleThreaded(client,
-                             base::MessageLoopProxy::current(),
+    InitializeSingleThreaded(client, base::ThreadTaskRunnerHandle::Get(),
                              nullptr);
   }
 
@@ -941,14 +941,14 @@
     return LayerTreeHost::CreateSingleThreaded(
         &client_, &client_, shared_bitmap_manager_.get(),
         gpu_memory_buffer_manager_.get(), nullptr, LayerTreeSettings(),
-        base::MessageLoopProxy::current(), nullptr);
+        base::ThreadTaskRunnerHandle::Get(), nullptr);
   }
 
   scoped_ptr<LayerTreeHost> Create(LayerTreeSettings settings) {
     return LayerTreeHost::CreateSingleThreaded(
         &client_, &client_, shared_bitmap_manager_.get(),
         gpu_memory_buffer_manager_.get(), nullptr, settings,
-        base::MessageLoopProxy::current(), nullptr);
+        base::ThreadTaskRunnerHandle::Get(), nullptr);
   }
 
  private:
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc
index 87c4e58..5814bb4 100644
--- a/cc/layers/picture_image_layer_impl_unittest.cc
+++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/picture_image_layer_impl.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/layers/append_quads_data.h"
 #include "cc/quads/draw_quad.h"
 #include "cc/resources/tile_priority.h"
@@ -35,7 +36,7 @@
 class PictureImageLayerImplTest : public testing::Test {
  public:
   PictureImageLayerImplTest()
-      : proxy_(base::MessageLoopProxy::current()),
+      : proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(ImplSidePaintingSettings(),
                    &proxy_,
                    &shared_bitmap_manager_,
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index 5369638..9ad6a24 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/picture_layer_impl.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/debug/lap_timer.h"
 #include "cc/resources/tiling_set_raster_queue_all.h"
 #include "cc/test/fake_impl_proxy.h"
@@ -39,7 +40,7 @@
 class PictureLayerImplPerfTest : public testing::Test {
  public:
   PictureLayerImplPerfTest()
-      : proxy_(base::MessageLoopProxy::current()),
+      : proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(ImplSidePaintingSettings(),
                    &proxy_,
                    &shared_bitmap_manager_,
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 4db0f5b5f..966186f 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -9,6 +9,8 @@
 #include <set>
 #include <utility>
 
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/base/math_util.h"
 #include "cc/layers/append_quads_data.h"
 #include "cc/layers/picture_layer.h"
@@ -71,7 +73,7 @@
 class PictureLayerImplTest : public testing::Test {
  public:
   PictureLayerImplTest()
-      : proxy_(base::MessageLoopProxy::current()),
+      : proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(LowResTilingsSettings(),
                    &proxy_,
                    &shared_bitmap_manager_,
@@ -85,7 +87,7 @@
   }
 
   explicit PictureLayerImplTest(const LayerTreeSettings& settings)
-      : proxy_(base::MessageLoopProxy::current()),
+      : proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(settings,
                    &proxy_,
                    &shared_bitmap_manager_,
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc
index 48d4abc..2f64693 100644
--- a/cc/layers/picture_layer_unittest.cc
+++ b/cc/layers/picture_layer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/picture_layer.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/layers/content_layer_client.h"
 #include "cc/layers/picture_layer_impl.h"
 #include "cc/resources/resource_update_queue.h"
@@ -130,12 +131,12 @@
 
   scoped_ptr<LayerTreeHost> host1 = LayerTreeHost::CreateSingleThreaded(
       &host_client1, &host_client1, shared_bitmap_manager.get(), nullptr,
-      nullptr, settings, base::MessageLoopProxy::current(), nullptr);
+      nullptr, settings, base::ThreadTaskRunnerHandle::Get(), nullptr);
   host_client1.SetLayerTreeHost(host1.get());
 
   scoped_ptr<LayerTreeHost> host2 = LayerTreeHost::CreateSingleThreaded(
       &host_client2, &host_client2, shared_bitmap_manager.get(), nullptr,
-      nullptr, settings, base::MessageLoopProxy::current(), nullptr);
+      nullptr, settings, base::ThreadTaskRunnerHandle::Get(), nullptr);
   host_client2.SetLayerTreeHost(host2.get());
 
   // The PictureLayer is put in one LayerTreeHost.
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 009ec3b7..cee0c9b 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/containers/hash_tables.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/animation/scrollbar_animation_controller.h"
 #include "cc/layers/append_quads_data.h"
 #include "cc/layers/painted_scrollbar_layer.h"
@@ -68,7 +69,7 @@
         next_id_(1),
         total_ui_resource_created_(0),
         total_ui_resource_deleted_(0) {
-    InitializeSingleThreaded(client, base::MessageLoopProxy::current(),
+    InitializeSingleThreaded(client, base::ThreadTaskRunnerHandle::Get(),
                              nullptr);
   }
 
diff --git a/cc/layers/surface_layer_unittest.cc b/cc/layers/surface_layer_unittest.cc
index 285acee..6661a1a2 100644
--- a/cc/layers/surface_layer_unittest.cc
+++ b/cc/layers/surface_layer_unittest.cc
@@ -5,7 +5,9 @@
 #include <set>
 #include <vector>
 
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/surface_layer.h"
 #include "cc/test/fake_impl_proxy.h"
@@ -173,7 +175,7 @@
   }
 
   void DidCommitAndDrawFrame() override {
-    base::MessageLoopProxy::current()->PostTask(
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(&SurfaceLayerSwapPromise::ChangeTree,
                               base::Unretained(this)));
   }
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index b088f6f..3bd90ec 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -9,7 +9,10 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "cc/layers/solid_color_layer.h"
@@ -53,8 +56,7 @@
  public:
   explicit MockLayerTreeHost(FakeLayerTreeHostClient* client)
       : LayerTreeHost(client, nullptr, nullptr, nullptr, LayerTreeSettings()) {
-    InitializeSingleThreaded(client,
-                             base::MessageLoopProxy::current(),
+    InitializeSingleThreaded(client, base::ThreadTaskRunnerHandle::Get(),
                              nullptr);
   }
 
@@ -403,10 +405,9 @@
   TextureLayerMailboxHolderTest()
       : main_thread_("MAIN") {
     main_thread_.Start();
-    main_thread_.message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&TextureLayerMailboxHolderTest::InitializeOnMain,
-                   base::Unretained(this)));
+    main_thread_.message_loop()->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::InitializeOnMain,
+                              base::Unretained(this)));
     Wait(main_thread_);
   }
 
@@ -414,7 +415,7 @@
     bool manual_reset = false;
     bool initially_signaled = false;
     base::WaitableEvent event(manual_reset, initially_signaled);
-    thread.message_loop()->PostTask(
+    thread.message_loop()->task_runner()->PostTask(
         FROM_HERE,
         base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
     event.Wait();
@@ -445,7 +446,7 @@
  protected:
   void InitializeOnMain() {
     main_thread_task_runner_ =
-        BlockingTaskRunner::Create(main_thread_.message_loop_proxy());
+        BlockingTaskRunner::Create(main_thread_.task_runner());
   }
 
   scoped_ptr<TestMailboxHolder::MainThreadReference>
@@ -459,30 +460,25 @@
       TextureLayer::CreateForMailbox(nullptr);
   ASSERT_TRUE(test_layer.get());
 
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+                            base::Unretained(this)));
 
   Wait(main_thread_);
 
   // The texture layer is attached to compositor1, and passes a reference to its
   // impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor1;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor1));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor1));
 
   // Then the texture layer is removed and attached to compositor2, and passes a
   // reference to its impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor2;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor2));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor2));
 
   Wait(main_thread_);
   Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
@@ -502,10 +498,9 @@
   EXPECT_CALL(test_data_.mock_callback_,
               Release(test_data_.mailbox_name1_, 200, false)).Times(1);
 
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+                            base::Unretained(this)));
   Wait(main_thread_);
   Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
 }
@@ -515,30 +510,25 @@
       TextureLayer::CreateForMailbox(nullptr);
   ASSERT_TRUE(test_layer.get());
 
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+                            base::Unretained(this)));
 
   Wait(main_thread_);
 
   // The texture layer is attached to compositor1, and passes a reference to its
   // impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor1;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor1));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor1));
 
   // Then the texture layer is removed and attached to compositor2, and passes a
   // reference to its impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor2;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor2));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor2));
 
   Wait(main_thread_);
   Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
@@ -547,10 +537,9 @@
   compositor1->Run(100, false, main_thread_task_runner_.get());
 
   // Then the main thread reference is destroyed.
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+                            base::Unretained(this)));
 
   Wait(main_thread_);
 
@@ -572,39 +561,33 @@
       TextureLayer::CreateForMailbox(nullptr);
   ASSERT_TRUE(test_layer.get());
 
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+                            base::Unretained(this)));
 
   Wait(main_thread_);
 
   // The texture layer is attached to compositor1, and passes a reference to its
   // impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor1;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor1));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor1));
 
   // Then the texture layer is removed and attached to compositor2, and passes a
   // reference to its impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor2;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor2));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor2));
 
   Wait(main_thread_);
   Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
 
   // The main thread reference is destroyed first.
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+                            base::Unretained(this)));
 
   // One compositor destroys their impl tree.
   compositor2->Run(200, false, main_thread_task_runner_.get());
@@ -629,39 +612,33 @@
       TextureLayer::CreateForMailbox(nullptr);
   ASSERT_TRUE(test_layer.get());
 
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+                            base::Unretained(this)));
 
   Wait(main_thread_);
 
   // The texture layer is attached to compositor1, and passes a reference to its
   // impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor1;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor1));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor1));
 
   // Then the texture layer is removed and attached to compositor2, and passes a
   // reference to its impl tree.
   scoped_ptr<SingleReleaseCallbackImpl> compositor2;
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
-                 base::Unretained(this),
-                 &compositor2));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                            base::Unretained(this), &compositor2));
 
   Wait(main_thread_);
   Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
 
   // The main thread reference is destroyed first.
-  main_thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
-                 base::Unretained(this)));
+  main_thread_.message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+                            base::Unretained(this)));
 
   EXPECT_CALL(test_data_.mock_callback_,
               Release(test_data_.mailbox_name1_, 200, true)).Times(1);
@@ -674,12 +651,10 @@
 
   // Post a task to start capturing tasks on the main thread. This will block
   // the main thread until we signal the |stop_capture| event.
-  main_thread_.message_loop()->PostTask(
+  main_thread_.message_loop()->task_runner()->PostTask(
       FROM_HERE,
       base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait,
-                 base::Unretained(this),
-                 &begin_capture,
-                 &wait_for_capture,
+                 base::Unretained(this), &begin_capture, &wait_for_capture,
                  &stop_capture));
 
   // Before the main thread capturing starts, one compositor destroys their
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc
index 511df0a..b81e031 100644
--- a/cc/layers/tiled_layer_unittest.cc
+++ b/cc/layers/tiled_layer_unittest.cc
@@ -7,7 +7,10 @@
 #include <limits>
 #include <vector>
 
+#include "base/location.h"
 #include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/resources/bitmap_content_layer_updater.h"
 #include "cc/resources/layer_painter.h"
 #include "cc/resources/prioritized_resource_manager.h"
@@ -51,10 +54,8 @@
       : FakeLayerTreeHostClient(FakeLayerTreeHostClient::DIRECT_3D) {}
 
   bool EnsureOutputSurfaceCreated() {
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        run_loop_.QuitClosure(),
-        base::TimeDelta::FromSeconds(5));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop_.QuitClosure(), base::TimeDelta::FromSeconds(5));
     run_loop_.Run();
     return output_surface_created_;
   }
@@ -95,8 +96,8 @@
     shared_bitmap_manager_.reset(new TestSharedBitmapManager());
     layer_tree_host_ = LayerTreeHost::CreateThreaded(
         &synchonous_output_surface_client_, shared_bitmap_manager_.get(),
-        nullptr, nullptr, settings_, base::MessageLoopProxy::current(),
-        impl_thread_.message_loop_proxy(), nullptr);
+        nullptr, nullptr, settings_, base::ThreadTaskRunnerHandle::Get(),
+        impl_thread_.task_runner(), nullptr);
     synchonous_output_surface_client_.SetLayerTreeHost(layer_tree_host_.get());
     proxy_ = layer_tree_host_->proxy();
     resource_manager_ = PrioritizedResourceManager::Create(proxy_);
diff --git a/cc/layers/ui_resource_layer_unittest.cc b/cc/layers/ui_resource_layer_unittest.cc
index a8cf77e..d0d356d 100644
--- a/cc/layers/ui_resource_layer_unittest.cc
+++ b/cc/layers/ui_resource_layer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/layers/ui_resource_layer.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/resources/prioritized_resource_manager.h"
 #include "cc/resources/resource_provider.h"
 #include "cc/resources/resource_update_queue.h"
@@ -53,9 +54,7 @@
   void SetUp() override {
     layer_tree_host_ = FakeLayerTreeHost::Create(&fake_client_);
     layer_tree_host_->InitializeSingleThreaded(
-        &fake_client_,
-        base::MessageLoopProxy::current(),
-        nullptr);
+        &fake_client_, base::ThreadTaskRunnerHandle::Get(), nullptr);
   }
 
   void TearDown() override {
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 7e0f8e0f5..57c8481 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -6,6 +6,9 @@
 
 #include <set>
 
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/base/math_util.h"
 #include "cc/output/compositor_frame_metadata.h"
 #include "cc/resources/resource_provider.h"
@@ -2032,7 +2035,7 @@
   // Make the sync point happen.
   gl->Finish();
   // Post a task after the sync point.
-  base::MessageLoop::current()->PostTask(
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(&OtherCallback, &other_callback_count));
 
   base::MessageLoop::current()->Run();
@@ -2061,7 +2064,7 @@
   // Make the sync point happen.
   gl->Finish();
   // Post a task after the sync point.
-  base::MessageLoop::current()->PostTask(
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(&OtherCallback, &other_callback_count));
 
   base::MessageLoop::current()->Run();
diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc
index c91352a4..5a6459c 100644
--- a/cc/output/output_surface.cc
+++ b/cc/output/output_surface.cc
@@ -5,7 +5,9 @@
 #include "cc/output/output_surface.h"
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/output/managed_memory_policy.h"
 #include "cc/output/output_surface_client.h"
@@ -182,10 +184,9 @@
 }
 
 void OutputSurface::PostSwapBuffersComplete() {
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&OutputSurface::OnSwapBuffersComplete,
-                 weak_ptr_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&OutputSurface::OnSwapBuffersComplete,
+                            weak_ptr_factory_.GetWeakPtr()));
 }
 
 // We don't post tasks bound to the client directly since they might run
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index 2f3de43..df3a69a5 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -236,8 +236,6 @@
       return false;
     case ResourceProvider::RESOURCE_TYPE_BITMAP:
       return true;
-    case ResourceProvider::RESOURCE_TYPE_INVALID:
-      break;
   }
 
   LOG(FATAL) << "Invalid resource type.";
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 6f634a9..996370d 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -243,43 +243,6 @@
 
 }  // namespace
 
-ResourceProvider::Resource::Resource()
-    : child_id(0),
-      gl_id(0),
-      gl_pixel_buffer_id(0),
-      gl_upload_query_id(0),
-      gl_read_lock_query_id(0),
-      pixels(NULL),
-      lock_for_read_count(0),
-      imported_count(0),
-      exported_count(0),
-      dirty_image(false),
-      locked_for_write(false),
-      lost(false),
-      marked_for_deletion(false),
-      pending_set_pixels(false),
-      set_pixels_completion_forced(false),
-      allocated(false),
-      read_lock_fences_enabled(false),
-      has_shared_bitmap_id(false),
-      allow_overlay(false),
-      read_lock_fence(NULL),
-      size(),
-      origin(INTERNAL),
-      target(0),
-      original_filter(0),
-      filter(0),
-      image_id(0),
-      bound_image_id(0),
-      texture_pool(0),
-      wrap_mode(0),
-      hint(TEXTURE_HINT_IMMUTABLE),
-      type(RESOURCE_TYPE_INVALID),
-      format(RGBA_8888),
-      shared_bitmap(NULL),
-      gpu_memory_buffer(NULL) {
-}
-
 ResourceProvider::Resource::~Resource() {}
 
 ResourceProvider::Resource::Resource(GLuint texture_id,
@@ -418,7 +381,9 @@
   DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
 }
 
-ResourceProvider::Child::Child() : marked_for_deletion(false) {}
+ResourceProvider::Child::Child()
+    : marked_for_deletion(false), needs_sync_points(true) {
+}
 
 ResourceProvider::Child::~Child() {}
 
@@ -430,21 +395,23 @@
     int highp_threshold_min,
     bool use_rgba_4444_texture_format,
     size_t id_allocation_chunk_size) {
-  scoped_ptr<ResourceProvider> resource_provider(
-      new ResourceProvider(output_surface,
-                           shared_bitmap_manager,
-                           gpu_memory_buffer_manager,
-                           blocking_main_thread_task_runner,
-                           highp_threshold_min,
-                           use_rgba_4444_texture_format,
-                           id_allocation_chunk_size));
+  ContextProvider* context_provider = output_surface->context_provider();
+  GLES2Interface* gl =
+      context_provider ? context_provider->ContextGL() : nullptr;
+  ResourceType default_resource_type =
+      gl ? RESOURCE_TYPE_GL_TEXTURE : RESOURCE_TYPE_BITMAP;
 
-  if (resource_provider->ContextGL())
+  scoped_ptr<ResourceProvider> resource_provider(new ResourceProvider(
+      output_surface, shared_bitmap_manager, gpu_memory_buffer_manager,
+      blocking_main_thread_task_runner, highp_threshold_min,
+      default_resource_type, use_rgba_4444_texture_format,
+      id_allocation_chunk_size));
+
+  if (gl)
     resource_provider->InitializeGL();
   else
     resource_provider->InitializeSoftware();
 
-  DCHECK_NE(RESOURCE_TYPE_INVALID, resource_provider->default_resource_type());
   return resource_provider.Pass();
 }
 
@@ -510,8 +477,6 @@
     case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case RESOURCE_TYPE_INVALID:
-      break;
   }
 
   LOG(FATAL) << "Invalid default resource type.";
@@ -536,8 +501,6 @@
     case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case RESOURCE_TYPE_INVALID:
-      break;
   }
 
   LOG(FATAL) << "Invalid default resource type.";
@@ -556,10 +519,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0, size, Resource::INTERNAL, target, GL_LINEAR,
-                    texture_pool, wrap_mode, hint, format);
-  resource.allocated = false;
-  resources_[id] = resource;
+  Resource* resource = InsertResource(
+      id, Resource(0, size, Resource::INTERNAL, target, GL_LINEAR, texture_pool,
+                   wrap_mode, hint, format));
+  resource->allocated = false;
   return id;
 }
 
@@ -573,10 +536,10 @@
   DCHECK(pixels);
 
   ResourceId id = next_id_++;
-  Resource resource(pixels, bitmap.release(), size, Resource::INTERNAL,
-                    GL_LINEAR, wrap_mode);
-  resource.allocated = true;
-  resources_[id] = resource;
+  Resource* resource =
+      InsertResource(id, Resource(pixels, bitmap.release(), size,
+                                  Resource::INTERNAL, GL_LINEAR, wrap_mode));
+  resource->allocated = true;
   return id;
 }
 
@@ -586,18 +549,17 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0, gfx::Size(), Resource::INTERNAL,
-                    GL_TEXTURE_RECTANGLE_ARB, GL_LINEAR,
-                    GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, GL_CLAMP_TO_EDGE,
-                    TEXTURE_HINT_IMMUTABLE, RGBA_8888);
-  LazyCreate(&resource);
+  Resource* resource = InsertResource(
+      id, Resource(0, gfx::Size(), Resource::INTERNAL, GL_TEXTURE_RECTANGLE_ARB,
+                   GL_LINEAR, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
+                   GL_CLAMP_TO_EDGE, TEXTURE_HINT_IMMUTABLE, RGBA_8888));
+  LazyCreate(resource);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
-  gl->BindTexture(GL_TEXTURE_RECTANGLE_ARB, resource.gl_id);
+  gl->BindTexture(GL_TEXTURE_RECTANGLE_ARB, resource->gl_id);
   gl->TexImageIOSurface2DCHROMIUM(
       GL_TEXTURE_RECTANGLE_ARB, size.width(), size.height(), io_surface_id, 0);
-  resource.allocated = true;
-  resources_[id] = resource;
+  resource->allocated = true;
   return id;
 }
 
@@ -608,25 +570,27 @@
   // Just store the information. Mailbox will be consumed in LockForRead().
   ResourceId id = next_id_++;
   DCHECK(mailbox.IsValid());
-  Resource& resource = resources_[id];
+  Resource* resource = nullptr;
   if (mailbox.IsTexture()) {
-    resource = Resource(0, gfx::Size(), Resource::EXTERNAL, mailbox.target(),
-                        mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR, 0,
-                        GL_CLAMP_TO_EDGE, TEXTURE_HINT_IMMUTABLE, RGBA_8888);
+    resource = InsertResource(
+        id, Resource(0, gfx::Size(), Resource::EXTERNAL, mailbox.target(),
+                     mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR, 0,
+                     GL_CLAMP_TO_EDGE, TEXTURE_HINT_IMMUTABLE, RGBA_8888));
   } else {
     DCHECK(mailbox.IsSharedMemory());
     SharedBitmap* shared_bitmap = mailbox.shared_bitmap();
     uint8_t* pixels = shared_bitmap->pixels();
     DCHECK(pixels);
-    resource = Resource(pixels, shared_bitmap, mailbox.shared_memory_size(),
-                        Resource::EXTERNAL, GL_LINEAR, GL_CLAMP_TO_EDGE);
+    resource = InsertResource(
+        id, Resource(pixels, shared_bitmap, mailbox.shared_memory_size(),
+                     Resource::EXTERNAL, GL_LINEAR, GL_CLAMP_TO_EDGE));
   }
-  resource.allocated = true;
-  resource.mailbox = mailbox;
-  resource.release_callback_impl =
+  resource->allocated = true;
+  resource->mailbox = mailbox;
+  resource->release_callback_impl =
       base::Bind(&SingleReleaseCallbackImpl::Run,
                  base::Owned(release_callback_impl.release()));
-  resource.allow_overlay = mailbox.allow_overlay();
+  resource->allow_overlay = mailbox.allow_overlay();
   return id;
 }
 
@@ -888,6 +852,15 @@
   return gfx::FrameTime::Now() + upload_one_texture_time * total_uploads;
 }
 
+ResourceProvider::Resource* ResourceProvider::InsertResource(
+    ResourceId id,
+    const Resource& resource) {
+  std::pair<ResourceMap::iterator, bool> result =
+      resources_.insert(ResourceMap::value_type(id, resource));
+  DCHECK(result.second);
+  return &result.first->second;
+}
+
 ResourceProvider::Resource* ResourceProvider::GetResource(ResourceId id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   // TODO(danakj): crbug.com/455931
@@ -1223,6 +1196,7 @@
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
     BlockingTaskRunner* blocking_main_thread_task_runner,
     int highp_threshold_min,
+    ResourceType default_resource_type,
     bool use_rgba_4444_texture_format,
     size_t id_allocation_chunk_size)
     : output_surface_(output_surface),
@@ -1233,7 +1207,7 @@
       highp_threshold_min_(highp_threshold_min),
       next_id_(1),
       next_child_(1),
-      default_resource_type_(RESOURCE_TYPE_INVALID),
+      default_resource_type_(default_resource_type),
       use_texture_storage_ext_(false),
       use_texture_format_bgra_(false),
       use_texture_usage_hint_(false),
@@ -1250,9 +1224,7 @@
 
 void ResourceProvider::InitializeSoftware() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_NE(RESOURCE_TYPE_BITMAP, default_resource_type_);
-
-  default_resource_type_ = RESOURCE_TYPE_BITMAP;
+  DCHECK_EQ(default_resource_type_, RESOURCE_TYPE_BITMAP);
   // Pick an arbitrary limit here similar to what hardware might.
   max_texture_size_ = 16 * 1024;
   best_texture_format_ = RGBA_8888;
@@ -1260,13 +1232,11 @@
 
 void ResourceProvider::InitializeGL() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(default_resource_type_, RESOURCE_TYPE_GL_TEXTURE);
   DCHECK(!texture_uploader_);
-  DCHECK_NE(RESOURCE_TYPE_GL_TEXTURE, default_resource_type_);
   DCHECK(!texture_id_allocator_);
   DCHECK(!buffer_id_allocator_);
 
-  default_resource_type_ = RESOURCE_TYPE_GL_TEXTURE;
-
   const ContextProvider::Capabilities& caps =
       output_surface_->context_provider()->ContextCapabilities();
 
@@ -1303,6 +1273,12 @@
   return child;
 }
 
+void ResourceProvider::SetChildNeedsSyncPoints(int child_id, bool needs) {
+  ChildMap::iterator it = children_.find(child_id);
+  DCHECK(it != children_.end());
+  it->second.needs_sync_points = needs;
+}
+
 void ResourceProvider::DestroyChild(int child_id) {
   ChildMap::iterator it = children_.find(child_id);
   DCHECK(it != children_.end());
@@ -1380,9 +1356,9 @@
     ResourceIdMap::iterator resource_in_map_it =
         child_info.child_to_parent_map.find(it->id);
     if (resource_in_map_it != child_info.child_to_parent_map.end()) {
-      Resource& resource = resources_[resource_in_map_it->second];
-      resource.marked_for_deletion = false;
-      resource.imported_count++;
+      Resource* resource = GetResource(resource_in_map_it->second);
+      resource->marked_for_deletion = false;
+      resource->imported_count++;
       continue;
     }
 
@@ -1397,25 +1373,27 @@
     }
 
     ResourceId local_id = next_id_++;
-    Resource& resource = resources_[local_id];
+    Resource* resource = nullptr;
     if (it->is_software) {
-      resource =
+      resource = InsertResource(
+          local_id,
           Resource(it->mailbox_holder.mailbox, it->size, Resource::DELEGATED,
-                   GL_LINEAR, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE);
+                   GL_LINEAR, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE));
     } else {
-      resource = Resource(0, it->size, Resource::DELEGATED,
-                          it->mailbox_holder.texture_target, it->filter, 0,
-                          it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE,
-                          TEXTURE_HINT_IMMUTABLE, it->format);
-      resource.mailbox = TextureMailbox(it->mailbox_holder.mailbox,
-                                        it->mailbox_holder.texture_target,
-                                        it->mailbox_holder.sync_point);
+      resource = InsertResource(
+          local_id, Resource(0, it->size, Resource::DELEGATED,
+                             it->mailbox_holder.texture_target, it->filter, 0,
+                             it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE,
+                             TEXTURE_HINT_IMMUTABLE, it->format));
+      resource->mailbox = TextureMailbox(it->mailbox_holder.mailbox,
+                                         it->mailbox_holder.texture_target,
+                                         it->mailbox_holder.sync_point);
     }
-    resource.child_id = child;
+    resource->child_id = child;
     // Don't allocate a texture for a child.
-    resource.allocated = true;
-    resource.imported_count = 1;
-    resource.allow_overlay = it->allow_overlay;
+    resource->allocated = true;
+    resource->imported_count = 1;
+    resource->allow_overlay = it->allow_overlay;
     child_info.parent_to_child_map[local_id] = it->id;
     child_info.child_to_parent_map[it->id] = local_id;
   }
@@ -1438,7 +1416,7 @@
     DCHECK(it != child_info.child_to_parent_map.end());
 
     ResourceId local_id = it->second;
-    DCHECK(!resources_[local_id].marked_for_deletion);
+    DCHECK(!GetResource(local_id)->marked_for_deletion);
     child_info.in_use_resources.insert(local_id);
   }
 
@@ -1683,7 +1661,7 @@
     resource.imported_count = 0;
     DeleteResourceInternal(it, style);
   }
-  if (need_sync_point) {
+  if (need_sync_point && child_info->needs_sync_points) {
     DCHECK(gl);
     GLuint sync_point = gl->InsertSyncPointCHROMIUM();
     for (size_t i = 0; i < to_return.size(); ++i) {
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index b505f450..0bb241b 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -74,8 +74,7 @@
         TEXTURE_HINT_IMMUTABLE | TEXTURE_HINT_FRAMEBUFFER
   };
   enum ResourceType {
-    RESOURCE_TYPE_INVALID = 0,
-    RESOURCE_TYPE_GL_TEXTURE = 1,
+    RESOURCE_TYPE_GL_TEXTURE,
     RESOURCE_TYPE_BITMAP,
   };
 
@@ -175,6 +174,10 @@
   // Destroys accounting for the child, deleting all accounted resources.
   void DestroyChild(int child);
 
+  // Sets whether resources need sync points set on them when returned to this
+  // child. Defaults to true.
+  void SetChildNeedsSyncPoints(int child, bool needs_sync_points);
+
   // Gets the child->parent resource ID map.
   const ResourceIdMap& GetChildToParentMap(int child) const;
 
@@ -438,7 +441,6 @@
   struct Resource {
     enum Origin { INTERNAL, EXTERNAL, DELEGATED };
 
-    Resource();
     ~Resource();
     Resource(unsigned texture_id,
              const gfx::Size& size,
@@ -518,6 +520,7 @@
     ReturnCallback return_callback;
     ResourceIdSet in_use_resources;
     bool marked_for_deletion;
+    bool needs_sync_points;
   };
   typedef base::hash_map<int, Child> ChildMap;
 
@@ -531,12 +534,14 @@
                    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
                    BlockingTaskRunner* blocking_main_thread_task_runner,
                    int highp_threshold_min,
+                   ResourceType default_resource_type,
                    bool use_rgba_4444_texture_format,
                    size_t id_allocation_chunk_size);
 
   void InitializeSoftware();
   void InitializeGL();
 
+  Resource* InsertResource(ResourceId id, const Resource& resource);
   Resource* GetResource(ResourceId id);
   const Resource* LockForRead(ResourceId id);
   void UnlockForRead(ResourceId id);
@@ -582,7 +587,7 @@
   int next_child_;
   ChildMap children_;
 
-  ResourceType default_resource_type_;
+  const ResourceType default_resource_type_;
   bool use_texture_storage_ext_;
   bool use_texture_format_bgra_;
   bool use_texture_usage_hint_;
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 72a061f7..7ebc0e3 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -371,9 +371,6 @@
              lock_software.sk_bitmap()->getSize());
       break;
     }
-    case ResourceProvider::RESOURCE_TYPE_INVALID:
-      NOTREACHED();
-      break;
   }
 }
 
@@ -414,9 +411,6 @@
         child_output_surface_ = FakeOutputSurface::CreateSoftware(
             make_scoped_ptr(new SoftwareOutputDevice));
         break;
-      case ResourceProvider::RESOURCE_TYPE_INVALID:
-        NOTREACHED();
-        break;
     }
     CHECK(output_surface_->BindToClient(&output_surface_client_));
     CHECK(child_output_surface_->BindToClient(&child_output_surface_client_));
@@ -937,6 +931,7 @@
   ReturnedResourceArray returned_to_child;
   int child_id =
       resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+  resource_provider_->SetChildNeedsSyncPoints(child_id, false);
   {
     // Transfer some resources to the parent.
     ResourceProvider::ResourceIdArray resource_ids_to_transfer;
@@ -958,6 +953,35 @@
                                                       resource_ids_to_transfer);
   }
 
+  {
+    EXPECT_EQ(0u, returned_to_child.size());
+
+    // Transfer resources back from the parent to the child. Set no resources as
+    // being in use.
+    ResourceProvider::ResourceIdArray no_resources;
+    resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+    ASSERT_EQ(3u, returned_to_child.size());
+    std::map<ResourceProvider::ResourceId, unsigned int> returned_sync_points;
+    for (const auto& returned : returned_to_child)
+      returned_sync_points[returned.id] = returned.sync_point;
+
+    EXPECT_TRUE(returned_sync_points.find(id1) != returned_sync_points.end());
+    // No new sync point should be created transferring back.
+    EXPECT_TRUE(returned_sync_points.find(id1) != returned_sync_points.end());
+    EXPECT_EQ(0u, returned_sync_points[id1]);
+    EXPECT_TRUE(returned_sync_points.find(id2) != returned_sync_points.end());
+    EXPECT_EQ(0u, returned_sync_points[id2]);
+    // Original sync point given should be returned.
+    EXPECT_TRUE(returned_sync_points.find(id3) != returned_sync_points.end());
+    EXPECT_EQ(external_sync_point, returned_sync_points[id3]);
+    EXPECT_FALSE(returned_to_child[0].lost);
+    EXPECT_FALSE(returned_to_child[1].lost);
+    EXPECT_FALSE(returned_to_child[2].lost);
+    child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+    returned_to_child.clear();
+  }
+
   resource_provider_->DestroyChild(child_id);
 }
 
diff --git a/cc/resources/texture_mailbox_deleter_unittest.cc b/cc/resources/texture_mailbox_deleter_unittest.cc
index 05e33a3..15b42507 100644
--- a/cc/resources/texture_mailbox_deleter_unittest.cc
+++ b/cc/resources/texture_mailbox_deleter_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "cc/resources/texture_mailbox_deleter.h"
 
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/resources/single_release_callback.h"
 #include "cc/test/test_context_provider.h"
 #include "cc/test/test_web_graphics_context_3d.h"
@@ -15,7 +16,7 @@
 
 TEST(TextureMailboxDeleterTest, Destroy) {
   scoped_ptr<TextureMailboxDeleter> deleter(
-      new TextureMailboxDeleter(base::MessageLoopProxy::current()));
+      new TextureMailboxDeleter(base::ThreadTaskRunnerHandle::Get()));
 
   scoped_refptr<TestContextProvider> context_provider =
       TestContextProvider::Create();
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc
index 236f91853..0913666 100644
--- a/cc/resources/tile_manager_perftest.cc
+++ b/cc/resources/tile_manager_perftest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "cc/debug/lap_timer.h"
 #include "cc/resources/raster_buffer.h"
@@ -88,7 +90,7 @@
       : memory_limit_policy_(ALLOW_ANYTHING),
         max_tiles_(10000),
         id_(7),
-        proxy_(base::MessageLoopProxy::current()),
+        proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(ImplSidePaintingSettings(10000),
                    &proxy_,
                    &shared_bitmap_manager_,
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc
index cce307c..8b830bf 100644
--- a/cc/resources/tile_manager_unittest.cc
+++ b/cc/resources/tile_manager_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/resources/eviction_tile_priority_queue.h"
 #include "cc/resources/raster_tile_priority_queue.h"
 #include "cc/resources/resource_pool.h"
@@ -39,7 +40,7 @@
         max_tiles_(10000),
         ready_to_activate_(false),
         id_(7),
-        proxy_(base::MessageLoopProxy::current()),
+        proxy_(base::ThreadTaskRunnerHandle::Get()),
         host_impl_(LowResTilingsSettings(),
                    &proxy_,
                    &shared_bitmap_manager_,
diff --git a/cc/resources/tile_task_worker_pool_unittest.cc b/cc/resources/tile_task_worker_pool_unittest.cc
index c581e97..34de309 100644
--- a/cc/resources/tile_task_worker_pool_unittest.cc
+++ b/cc/resources/tile_task_worker_pool_unittest.cc
@@ -8,6 +8,9 @@
 #include <vector>
 
 #include "base/cancelable_callback.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/base/unique_notifier.h"
 #include "cc/resources/bitmap_tile_task_worker_pool.h"
 #include "cc/resources/gpu_rasterizer.h"
@@ -131,7 +134,7 @@
       : context_provider_(TestContextProvider::Create()),
         worker_context_provider_(TestContextProvider::Create()),
         all_tile_tasks_finished_(
-            base::MessageLoopProxy::current().get(),
+            base::ThreadTaskRunnerHandle::Get().get(),
             base::Bind(&TileTaskWorkerPoolTest::AllTileTasksFinished,
                        base::Unretained(this))),
         timeout_seconds_(5),
@@ -143,14 +146,14 @@
       case TILE_TASK_WORKER_POOL_TYPE_PIXEL_BUFFER:
         Create3dOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = PixelBufferTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_,
             context_provider_.get(), resource_provider_.get(),
             kMaxTransferBufferUsageBytes);
         break;
       case TILE_TASK_WORKER_POOL_TYPE_ZERO_COPY:
         Create3dOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = ZeroCopyTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_,
             resource_provider_.get());
         break;
       case TILE_TASK_WORKER_POOL_TYPE_ONE_COPY:
@@ -158,20 +161,20 @@
         staging_resource_pool_ = ResourcePool::Create(resource_provider_.get(),
                                                       GL_TEXTURE_2D);
         tile_task_worker_pool_ = OneCopyTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_,
             context_provider_.get(), resource_provider_.get(),
             staging_resource_pool_.get());
         break;
       case TILE_TASK_WORKER_POOL_TYPE_GPU:
         Create3dOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = GpuTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_,
             context_provider_.get(), resource_provider_.get(), false, 0);
         break;
       case TILE_TASK_WORKER_POOL_TYPE_BITMAP:
         CreateSoftwareOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = BitmapTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            base::ThreadTaskRunnerHandle::Get().get(), &task_graph_runner_,
             resource_provider_.get());
         break;
     }
@@ -208,7 +211,7 @@
     if (timeout_seconds_) {
       timeout_.Reset(base::Bind(&TileTaskWorkerPoolTest::OnTimeout,
                                 base::Unretained(this)));
-      base::MessageLoopProxy::current()->PostDelayedTask(
+      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE, timeout_.callback(),
           base::TimeDelta::FromSeconds(timeout_seconds_));
     }
diff --git a/cc/surfaces/display.cc b/cc/surfaces/display.cc
index 71fb318..d520a66 100644
--- a/cc/surfaces/display.cc
+++ b/cc/surfaces/display.cc
@@ -4,7 +4,7 @@
 
 #include "cc/surfaces/display.h"
 
-#include "base/message_loop/message_loop.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/debug/benchmark_instrumentation.h"
 #include "cc/output/compositor_frame.h"
@@ -34,9 +34,9 @@
       settings_(settings),
       device_scale_factor_(1.f),
       blocking_main_thread_task_runner_(
-          BlockingTaskRunner::Create(base::MessageLoopProxy::current())),
+          BlockingTaskRunner::Create(base::ThreadTaskRunnerHandle::Get())),
       texture_mailbox_deleter_(
-          new TextureMailboxDeleter(base::MessageLoopProxy::current())) {
+          new TextureMailboxDeleter(base::ThreadTaskRunnerHandle::Get())) {
   manager_->AddObserver(this);
 }
 
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index 4a7d9524f..41bb632 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -120,6 +120,10 @@
   if (it == surface_id_to_resource_child_id_.end()) {
     int child_id =
         provider_->CreateChild(base::Bind(&UnrefHelper, surface->factory()));
+    if (surface->factory()) {
+      provider_->SetChildNeedsSyncPoints(
+          child_id, surface->factory()->needs_sync_points());
+    }
     surface_id_to_resource_child_id_[surface->surface_id()] = child_id;
     return child_id;
   } else {
diff --git a/cc/surfaces/surface_display_output_surface.cc b/cc/surfaces/surface_display_output_surface.cc
index 6769e741..ece25711 100644
--- a/cc/surfaces/surface_display_output_surface.cc
+++ b/cc/surfaces/surface_display_output_surface.cc
@@ -23,6 +23,7 @@
       surface_manager_(surface_manager),
       factory_(surface_manager, this),
       allocator_(allocator) {
+  factory_.set_needs_sync_points(false);
   capabilities_.delegated_rendering = true;
   capabilities_.max_frames_pending = 1;
   capabilities_.adjust_deadline_for_parent = true;
diff --git a/cc/surfaces/surface_factory.cc b/cc/surfaces/surface_factory.cc
index aead4f68..9bb50ac 100644
--- a/cc/surfaces/surface_factory.cc
+++ b/cc/surfaces/surface_factory.cc
@@ -13,7 +13,10 @@
 namespace cc {
 SurfaceFactory::SurfaceFactory(SurfaceManager* manager,
                                SurfaceFactoryClient* client)
-    : manager_(manager), client_(client), holder_(client) {
+    : manager_(manager),
+      client_(client),
+      holder_(client),
+      needs_sync_points_(true) {
 }
 
 SurfaceFactory::~SurfaceFactory() {
diff --git a/cc/surfaces/surface_factory.h b/cc/surfaces/surface_factory.h
index b6b627fb..c83fa32 100644
--- a/cc/surfaces/surface_factory.h
+++ b/cc/surfaces/surface_factory.h
@@ -63,11 +63,19 @@
 
   SurfaceManager* manager() { return manager_; }
 
+  // This can be set to false if resources from this SurfaceFactory don't need
+  // to have sync points set on them when returned from the Display, for
+  // example if the Display shares a context with the creator.
+  bool needs_sync_points() const { return needs_sync_points_; }
+  void set_needs_sync_points(bool needs) { needs_sync_points_ = needs; }
+
  private:
   SurfaceManager* manager_;
   SurfaceFactoryClient* client_;
   SurfaceResourceHolder holder_;
 
+  bool needs_sync_points_;
+
   typedef base::ScopedPtrHashMap<SurfaceId, Surface> OwningSurfaceMap;
   base::ScopedPtrHashMap<SurfaceId, Surface> surface_map_;
 
diff --git a/cc/test/fake_external_begin_frame_source.cc b/cc/test/fake_external_begin_frame_source.cc
index c1885c14..3c35536 100644
--- a/cc/test/fake_external_begin_frame_source.cc
+++ b/cc/test/fake_external_begin_frame_source.cc
@@ -5,7 +5,8 @@
 #include "cc/test/fake_external_begin_frame_source.h"
 
 #include "base/location.h"
-#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "cc/test/begin_frame_args_test.h"
 
@@ -45,7 +46,7 @@
 }
 
 void FakeExternalBeginFrameSource::PostTestOnBeginFrame() {
-  base::MessageLoop::current()->PostDelayedTask(
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE, base::Bind(&FakeExternalBeginFrameSource::TestOnBeginFrame,
                             weak_ptr_factory_.GetWeakPtr()),
       base::TimeDelta::FromMilliseconds(milliseconds_per_frame_));
diff --git a/cc/test/fake_impl_proxy.h b/cc/test/fake_impl_proxy.h
index 69eb5d21..6cbf9a3 100644
--- a/cc/test/fake_impl_proxy.h
+++ b/cc/test/fake_impl_proxy.h
@@ -5,20 +5,25 @@
 #ifndef CC_TEST_FAKE_IMPL_PROXY_H_
 #define CC_TEST_FAKE_IMPL_PROXY_H_
 
+#include "base/thread_task_runner_handle.h"
 #include "cc/test/fake_proxy.h"
 #include "cc/trees/single_thread_proxy.h"
 
+namespace base {
+class SingleThreadIdleTaskRunner;
+}
+
 namespace cc {
 
 class FakeImplProxy : public FakeProxy {
  public:
   FakeImplProxy()
-      : FakeProxy(base::MessageLoopProxy::current(), nullptr),
+      : FakeProxy(base::ThreadTaskRunnerHandle::Get(), nullptr),
         set_impl_thread_(this) {}
 
   explicit FakeImplProxy(
       scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
-      : FakeProxy(base::MessageLoopProxy::current(), impl_task_runner),
+      : FakeProxy(base::ThreadTaskRunnerHandle::Get(), impl_task_runner),
         set_impl_thread_(this) {}
 
  private:
diff --git a/cc/test/fake_tile_manager.cc b/cc/test/fake_tile_manager.cc
index d33093f..47634eea 100644
--- a/cc/test/fake_tile_manager.cc
+++ b/cc/test/fake_tile_manager.cc
@@ -8,6 +8,7 @@
 #include <limits>
 
 #include "base/lazy_instance.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/resources/raster_buffer.h"
 #include "cc/resources/tile_task_runner.h"
 
@@ -65,7 +66,7 @@
 
 FakeTileManager::FakeTileManager(TileManagerClient* client)
     : TileManager(client,
-                  base::MessageLoopProxy::current(),
+                  base::ThreadTaskRunnerHandle::Get(),
                   nullptr,
                   g_fake_tile_task_runner.Pointer(),
                   std::numeric_limits<size_t>::max()) {
@@ -74,7 +75,7 @@
 FakeTileManager::FakeTileManager(TileManagerClient* client,
                                  ResourcePool* resource_pool)
     : TileManager(client,
-                  base::MessageLoopProxy::current(),
+                  base::ThreadTaskRunnerHandle::Get(),
                   resource_pool,
                   g_fake_tile_task_runner.Pointer(),
                   std::numeric_limits<size_t>::max()) {
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index a39c452..93ddf12 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -5,6 +5,9 @@
 #include "cc/test/layer_tree_test.h"
 
 #include "base/command_line.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/animation/animation.h"
 #include "cc/animation/animation_registrar.h"
 #include "cc/animation/layer_animation_controller.h"
@@ -663,12 +666,12 @@
     external_begin_frame_source_ = external_begin_frame_source.get();
   }
 
-  DCHECK(!impl_thread_ || impl_thread_->message_loop_proxy().get());
+  DCHECK(!impl_thread_ || impl_thread_->task_runner().get());
   layer_tree_host_ = LayerTreeHostForTesting::Create(
       this, client_.get(), shared_bitmap_manager_.get(),
       gpu_memory_buffer_manager_.get(), task_graph_runner_.get(), settings_,
-      base::MessageLoopProxy::current(),
-      impl_thread_ ? impl_thread_->message_loop_proxy() : NULL,
+      base::ThreadTaskRunnerHandle::Get(),
+      impl_thread_ ? impl_thread_->task_runner() : NULL,
       external_begin_frame_source.Pass());
   ASSERT_TRUE(layer_tree_host_);
 
@@ -792,7 +795,7 @@
     ASSERT_TRUE(impl_thread_->Start());
   }
 
-  main_task_runner_ = base::MessageLoopProxy::current();
+  main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
   shared_bitmap_manager_.reset(new TestSharedBitmapManager);
   gpu_memory_buffer_manager_.reset(new TestGpuMemoryBufferManager);
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index c2dc5b5..2c3fb653 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -5,9 +5,9 @@
 #include "cc/test/pixel_test.h"
 
 #include "base/command_line.h"
-#include "base/message_loop/message_loop_proxy.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/base/switches.h"
 #include "cc/output/compositor_frame_metadata.h"
 #include "cc/output/copy_output_request.h"
@@ -37,7 +37,7 @@
       disable_picture_quad_image_filtering_(false),
       output_surface_client_(new FakeOutputSurfaceClient),
       main_thread_task_runner_(
-          BlockingTaskRunner::Create(base::MessageLoopProxy::current())) {
+          BlockingTaskRunner::Create(base::ThreadTaskRunnerHandle::Get())) {
 }
 PixelTest::~PixelTest() {}
 
@@ -143,7 +143,7 @@
                                1);
 
   texture_mailbox_deleter_ = make_scoped_ptr(
-      new TextureMailboxDeleter(base::MessageLoopProxy::current()));
+      new TextureMailboxDeleter(base::ThreadTaskRunnerHandle::Get()));
 
   renderer_ = GLRenderer::Create(
       this, &settings_.renderer_settings, output_surface_.get(),
diff --git a/cc/test/test_context_support.cc b/cc/test/test_context_support.cc
index f74f692..b3c45563 100644
--- a/cc/test/test_context_support.cc
+++ b/cc/test/test_context_support.cc
@@ -5,7 +5,9 @@
 #include "cc/test/test_context_support.h"
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 
 namespace cc {
 
@@ -18,19 +20,17 @@
 void TestContextSupport::SignalSyncPoint(uint32 sync_point,
                                          const base::Closure& callback) {
   sync_point_callbacks_.push_back(callback);
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&TestContextSupport::CallAllSyncPointCallbacks,
-                 weak_ptr_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&TestContextSupport::CallAllSyncPointCallbacks,
+                            weak_ptr_factory_.GetWeakPtr()));
 }
 
 void TestContextSupport::SignalQuery(uint32 query,
                                      const base::Closure& callback) {
   sync_point_callbacks_.push_back(callback);
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&TestContextSupport::CallAllSyncPointCallbacks,
-                 weak_ptr_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&TestContextSupport::CallAllSyncPointCallbacks,
+                            weak_ptr_factory_.GetWeakPtr()));
 }
 
 void TestContextSupport::SetSurfaceVisible(bool visible) {
@@ -41,8 +41,8 @@
 
 void TestContextSupport::CallAllSyncPointCallbacks() {
   for (size_t i = 0; i < sync_point_callbacks_.size(); ++i) {
-    base::MessageLoop::current()->PostTask(
-        FROM_HERE, sync_point_callbacks_[i]);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  sync_point_callbacks_[i]);
   }
   sync_point_callbacks_.clear();
 }
diff --git a/cc/trees/blocking_task_runner_unittest.cc b/cc/trees/blocking_task_runner_unittest.cc
index ba4f96a8..f837a7cc 100644
--- a/cc/trees/blocking_task_runner_unittest.cc
+++ b/cc/trees/blocking_task_runner_unittest.cc
@@ -5,7 +5,10 @@
 #include "cc/trees/blocking_task_runner.h"
 
 #include "base/bind.h"
+#include "base/location.h"
 #include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/test/ordered_simple_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -19,7 +22,7 @@
 TEST(BlockingTaskRunnerTest, NoCapture) {
   bool did_run = false;
   scoped_ptr<BlockingTaskRunner> runner(
-      BlockingTaskRunner::Create(base::MessageLoopProxy::current()));
+      BlockingTaskRunner::Create(base::ThreadTaskRunnerHandle::Get()));
   runner->PostTask(FROM_HERE, base::Bind(&TestTask, &did_run));
   EXPECT_FALSE(did_run);
   base::RunLoop().RunUntilIdle();
@@ -29,7 +32,7 @@
 TEST(BlockingTaskRunnerTest, Capture) {
   bool did_run = false;
   scoped_ptr<BlockingTaskRunner> runner(
-      BlockingTaskRunner::Create(base::MessageLoopProxy::current()));
+      BlockingTaskRunner::Create(base::ThreadTaskRunnerHandle::Get()));
   {
     BlockingTaskRunner::CapturePostTasks capture(runner.get());
     runner->PostTask(FROM_HERE, base::Bind(&TestTask, &did_run));
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index b4a268f..0e25f06 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -267,14 +267,34 @@
     gfx::Transform clip_to_target;
     gfx::Transform target_to_clip;
 
-    bool success =
-        transform_tree.ComputeTransform(parent_transform_node->id,
-                                        clip_node->data.target_id,
-                                        &parent_to_target) &&
-        transform_tree.ComputeTransform(
-            transform_node->id, clip_node->data.target_id, &clip_to_target) &&
-        transform_tree.ComputeTransform(clip_node->data.target_id,
-                                        transform_node->id, &target_to_clip);
+    const bool target_is_root_surface = clip_node->data.target_id == 1;
+    // When the target is the root surface, we need to include the root
+    // transform by walking up to the root of the transform tree.
+    const int target_id =
+        target_is_root_surface ? 0 : clip_node->data.target_id;
+
+    bool success = true;
+    if (parent_transform_node->data.content_target_id ==
+        clip_node->data.target_id) {
+      parent_to_target = parent_transform_node->data.to_target;
+    } else {
+      success &= transform_tree.ComputeTransformWithDestinationSublayerScale(
+          parent_transform_node->id, target_id, &parent_to_target);
+    }
+
+    if (transform_node->data.content_target_id == clip_node->data.target_id) {
+      clip_to_target = transform_node->data.to_target;
+    } else {
+      success &= transform_tree.ComputeTransformWithDestinationSublayerScale(
+          transform_node->id, target_id, &clip_to_target);
+    }
+
+    if (transform_node->data.content_target_id == clip_node->data.target_id &&
+        transform_node->data.ancestors_are_invertible) {
+      target_to_clip = transform_node->data.from_target;
+    } else {
+      success &= clip_to_target.GetInverse(&target_to_clip);
+    }
 
     // If we can't compute a transform, it's because we had to use the inverse
     // of a singular transform. We won't draw in this case, so there's no need
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 53bfcb8f..ac473ab 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -11,10 +11,12 @@
 #include "base/atomic_sequence_num.h"
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/message_loop/message_loop.h"
+#include "base/location.h"
 #include "base/metrics/histogram.h"
+#include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/animation/animation_registrar.h"
@@ -823,7 +825,7 @@
                                         base::Unretained(this)));
     static base::TimeDelta prepaint_delay =
         base::TimeDelta::FromMilliseconds(100);
-    base::MessageLoop::current()->PostDelayedTask(
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE, prepaint_callback_.callback(), prepaint_delay);
   }
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index fa0bfc34..fcb041e 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -11,6 +11,8 @@
 #include "base/command_line.h"
 #include "base/containers/hash_tables.h"
 #include "base/containers/scoped_ptr_hash_map.h"
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/animation/scrollbar_animation_controller_thinning.h"
 #include "cc/base/math_util.h"
 #include "cc/input/page_scale_animation.h"
@@ -81,8 +83,8 @@
                               public LayerTreeHostImplClient {
  public:
   LayerTreeHostImplTest()
-      : proxy_(base::MessageLoopProxy::current(),
-               base::MessageLoopProxy::current()),
+      : proxy_(base::ThreadTaskRunnerHandle::Get(),
+               base::ThreadTaskRunnerHandle::Get()),
         always_impl_thread_(&proxy_),
         always_main_thread_blocked_(&proxy_),
         shared_bitmap_manager_(new TestSharedBitmapManager),
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index ef70f825..51994c7 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -7,7 +7,10 @@
 #include <algorithm>
 
 #include "base/auto_reset.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/animation/timing_function.h"
 #include "cc/debug/frame_rate_counter.h"
 #include "cc/layers/content_layer.h"
@@ -2368,7 +2371,7 @@
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
       &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
-      base::MessageLoopProxy::current(), nullptr);
+      base::ThreadTaskRunnerHandle::Get(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2387,7 +2390,7 @@
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
       &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
-      base::MessageLoopProxy::current(), nullptr);
+      base::ThreadTaskRunnerHandle::Get(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2406,7 +2409,7 @@
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
       &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
-      base::MessageLoopProxy::current(), nullptr);
+      base::ThreadTaskRunnerHandle::Get(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2426,7 +2429,7 @@
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
       &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
-      base::MessageLoopProxy::current(), nullptr);
+      base::ThreadTaskRunnerHandle::Get(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
diff --git a/cc/trees/layer_tree_host_unittest_no_message_loop.cc b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
index 3abc4dc8..0b68ffb2 100644
--- a/cc/trees/layer_tree_host_unittest_no_message_loop.cc
+++ b/cc/trees/layer_tree_host_unittest_no_message_loop.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 "base/message_loop/message_loop_proxy.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/simple_thread.h"
 #include "cc/layers/delegated_frame_provider.h"
 #include "cc/layers/delegated_frame_resource_collection.h"
@@ -92,9 +92,9 @@
 
   // base::DelegateSimpleThread::Delegate override.
   void Run() override {
-    ASSERT_FALSE(base::MessageLoopProxy::current().get());
+    ASSERT_FALSE(base::ThreadTaskRunnerHandle::IsSet());
     RunTestWithoutMessageLoop();
-    EXPECT_FALSE(base::MessageLoopProxy::current().get());
+    EXPECT_FALSE(base::ThreadTaskRunnerHandle::IsSet());
   }
 
  protected:
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index be972e9..46cc3e6 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -4,7 +4,10 @@
 
 #include "cc/trees/layer_tree_host.h"
 
+#include "base/location.h"
 #include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
 #include "cc/layers/picture_layer.h"
@@ -1120,20 +1123,18 @@
   LayerTreeSettings settings;
 
   ThreadCheckingInputHandlerClient input_handler_client(
-          impl_thread.message_loop_proxy().get(), &received_stop_flinging);
+      impl_thread.task_runner().get(), &received_stop_flinging);
   FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D);
 
-  ASSERT_TRUE(impl_thread.message_loop_proxy().get());
+  ASSERT_TRUE(impl_thread.task_runner().get());
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHost> layer_tree_host = LayerTreeHost::CreateThreaded(
       &client, shared_bitmap_manager.get(), NULL, NULL, settings,
-      base::MessageLoopProxy::current(), impl_thread.message_loop_proxy(),
-      nullptr);
+      base::ThreadTaskRunnerHandle::Get(), impl_thread.task_runner(), nullptr);
 
-  impl_thread.message_loop_proxy()
-      ->PostTask(FROM_HERE,
-                 base::Bind(&BindInputHandlerOnCompositorThread,
+  impl_thread.task_runner()->PostTask(
+      FROM_HERE, base::Bind(&BindInputHandlerOnCompositorThread,
                             layer_tree_host->GetInputHandler(),
                             base::Unretained(&input_handler_client)));
 
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index d013b12..0149befe 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -33,6 +33,7 @@
   float device_scale_factor;
   bool in_subtree_of_page_scale_application_layer;
   bool should_flatten;
+  bool ancestor_clips_subtree;
   const gfx::Transform* device_transform;
   gfx::Vector2dF scroll_compensation_adjustment;
 };
@@ -52,9 +53,10 @@
 
 static bool RequiresClipNode(Layer* layer,
                              const DataForRecursion& data,
-                             int parent_transform_id) {
+                             int parent_transform_id,
+                             bool is_clipped) {
   const bool render_surface_applies_clip =
-      layer->render_surface() && layer->is_clipped();
+      layer->render_surface() && is_clipped;
   const bool render_surface_may_grow_due_to_clip_children =
       layer->render_surface() && layer->num_unclipped_descendants() > 0;
 
@@ -72,6 +74,10 @@
   return !axis_aligned_with_respect_to_parent;
 }
 
+static bool LayerClipsSubtree(Layer* layer) {
+  return layer->masks_to_bounds() || layer->mask_layer();
+}
+
 void AddClipNodeIfNeeded(const DataForRecursion& data_from_ancestor,
                          Layer* layer,
                          bool created_transform_node,
@@ -79,17 +85,29 @@
   ClipNode* parent = GetClipParent(data_from_ancestor, layer);
   int parent_id = parent->id;
 
-  // TODO(vollick): once Andrew refactors the surface determinations out of
-  // CDP, the the layer->render_surface() check will be invalid.
-  const bool has_unclipped_surface =
-      layer->render_surface() &&
-      !layer->render_surface()->is_clipped() &&
-      layer->num_unclipped_descendants() == 0;
+  bool ancestor_clips_subtree =
+      data_from_ancestor.ancestor_clips_subtree || layer->clip_parent();
+
+  data_for_children->ancestor_clips_subtree = false;
+  bool has_unclipped_surface = false;
+
+  if (layer->has_render_surface()) {
+    if (ancestor_clips_subtree && layer->num_unclipped_descendants() > 0)
+      data_for_children->ancestor_clips_subtree = true;
+    else if (!ancestor_clips_subtree && !layer->num_unclipped_descendants())
+      has_unclipped_surface = true;
+  } else {
+    data_for_children->ancestor_clips_subtree = ancestor_clips_subtree;
+  }
+
+  if (LayerClipsSubtree(layer))
+    data_for_children->ancestor_clips_subtree = true;
 
   if (has_unclipped_surface)
     parent_id = 0;
 
-  if (!RequiresClipNode(layer, data_from_ancestor, parent->data.transform_id)) {
+  if (!RequiresClipNode(layer, data_from_ancestor, parent->data.transform_id,
+                        data_for_children->ancestor_clips_subtree)) {
     // Unclipped surfaces reset the clip rect.
     data_for_children->clip_tree_parent = parent_id;
   } else if (layer->parent()) {
@@ -339,6 +357,7 @@
   data_for_recursion.device_scale_factor = device_scale_factor;
   data_for_recursion.in_subtree_of_page_scale_application_layer = false;
   data_for_recursion.should_flatten = false;
+  data_for_recursion.ancestor_clips_subtree = true;
   data_for_recursion.device_transform = &device_transform;
 
   ClipNode root_clip;
diff --git a/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
index da0cc9f..7cc1286 100644
--- a/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
+++ b/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
@@ -32,12 +32,6 @@
     /** Whether instant is disabled. */
     public static final String DISABLE_INSTANT = "disable-instant";
 
-    /** Whether force-enable the "hardware acceleration" preference. */
-    public static final String HARDWARE_ACCELERATION = "hardware-acceleration";
-
-    /** If specified, enables notification center verbose logging. */
-    public static final String NOTIFICATION_CENTER_LOGGING = "notification-center-logging";
-
     /** Enables StrictMode violation detection. By default this logs violations to logcat. */
     public static final String STRICT_MODE = "strict-mode";
 
@@ -50,18 +44,12 @@
     /** Force the crash dump to be uploaded regardless of preferences. */
     public static final String FORCE_CRASH_DUMP_UPLOAD = "force-dump-upload";
 
-    /** Do not use OAuth2 tokens for communication with Cloud Print service for Chrome to Mobile. */
-    public static final String DISABLE_CHROME_TO_MOBILE_OAUTH2 = "disable-chrome-to-mobile-oauth2";
-
     /** Enable debug logs for the video casting feature. */
     public static final String ENABLE_CAST_DEBUG_LOGS = "enable-cast-debug";
 
     /** Prevent automatic reconnection to current Cast video when Chrome restarts. */
     public static final String DISABLE_CAST_RECONNECTION = "disable-cast-reconnection";
 
-    /** Whether site/user triggered persistent fullscreen is supported. */
-    public static final String DISABLE_PERSISTENT_FULLSCREEN = "disable-persistent-fullscreen";
-
     /** Whether or not to enable the experimental tablet tab stack. */
     public static final String ENABLE_TABLET_TAB_STACK = "enable-tablet-tab-stack";
 
@@ -122,9 +110,6 @@
     public static final String EXPERIMENTAL_WEB_PLAFTORM_FEATURES =
             "enable-experimental-web-platform-features";
 
-    /** Enable the Reader Mode icon in toolbar. */
-    public static final String ENABLE_READER_MODE_TOOLBAR_ICON = "enable-reader-mode-toolbar-icon";
-
     /** Enable Reader Mode button animation. */
     public static final String ENABLE_READER_MODE_BUTTON_ANIMATION =
             "enable-dom-distiller-button-animation";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java
index 30cfae2..1e0ff7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java
@@ -31,9 +31,9 @@
 import android.util.LongSparseArray;
 
 import org.chromium.base.CalledByNative;
-import org.chromium.base.CalledByNativeUnchecked;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNativeUnchecked;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.chrome.browser.database.SQLiteCursor;
 import org.chromium.sync.AndroidSyncSettings;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
index 5c6fde54..e945c7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/OffTheRecordDocumentTabModel.java
@@ -143,7 +143,7 @@
 
     @Override
     public boolean setLastShownId(int id) {
-        ensureTabModelImpl();
+        if (!isDocumentTabModelImplCreated()) return false;
         return getDelegateDocumentTabModel().setLastShownId(id);
     }
 
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index a7218b7..d76ee63 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -672,6 +672,9 @@
           Your changes will take effect the next time you restart your device.
         </message>
       </if>
+      <message name="IDS_FLAGS_DEBUG_SHORTCUTS_DESCRIPTION" desc="Description of the 'Debugging keyboard shortcuts' lab.">
+        Enables additional keyboard shortcuts that are useful for debugging Chromium.
+      </message>
 
       <!-- Obsolete System info bar -->
       <message name="IDS_SYSTEM_OBSOLETE_MESSAGE" desc="Message shown when your OS is no longer supported. This message is followed by a 'Learn more' link.">
@@ -766,6 +769,11 @@
         </message>
       </if>
 
+      <!-- settings reset bubble messages -->
+      <message name="IDS_REPORT_BUBBLE_TEXT" desc="Text for the settings reset bubble reporting checkbox.">
+        Help make Chromium better by reporting the current settings
+      </message>
+
       <!-- chrome://settings/extensions page -->
       <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chromium cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode.">
         <ph name="BEGIN_BOLD">&lt;b&gt;</ph>Warning:<ph name="END_BOLD">&lt;/b&gt;</ph> Chromium cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 68fdfeba..c2a6190 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5713,9 +5713,6 @@
       <message name="IDS_FLAGS_DEBUG_SHORTCUTS_NAME" desc="Name of the 'Debugging keyboard shortcuts' lab.">
         Debugging keyboard shortcuts
       </message>
-      <message name="IDS_FLAGS_DEBUG_SHORTCUTS_DESCRIPTION" desc="Description of the 'Debugging keyboard shortcuts' lab.">
-        Enables additional keyboard shortcuts that are useful for debugging Chromium.
-      </message>
       <message name="IDS_FLAGS_IGNORE_GPU_BLACKLIST_NAME" desc="Name of the 'Ignore GPU blacklist' lab.">
         Override software rendering list
       </message>
@@ -8456,9 +8453,6 @@
       <message name="IDS_RESET_BUBBLE_TEXT" desc="Text for the settings reset bubble view full description.">
         <ph name="IDS_SHORT_PRODUCT_NAME">$1<ex>Chrome</ex></ph> detected that your browser settings may have been changed without your knowledge. Would you like to reset them to their original defaults?
       </message>
-      <message name="IDS_REPORT_BUBBLE_TEXT" desc="Text for the settings reset bubble reporting checkbox.">
-        Help make Google Chrome better by reporting the current settings
-      </message>
 
       <!-- SRT bubble messages -->
       <message name="IDS_SRT_BUBBLE_DOWNLOAD_BUTTON_TEXT" desc="Download button of the software removal tool bubble">
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index d76eac9..5168791f 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -595,6 +595,9 @@
           Your changes will take effect the next time you restart your device.
         </message>
       </if>
+      <message name="IDS_FLAGS_DEBUG_SHORTCUTS_DESCRIPTION" desc="Description of the 'Debugging keyboard shortcuts' lab.">
+        Enables additional keyboard shortcuts that are useful for debugging Google Chrome.
+      </message>
 
       <!-- Obsolete System info bar -->
       <message name="IDS_SYSTEM_OBSOLETE_MESSAGE" desc="Message shown when your OS is no longer supported. This message is followed by a 'Learn more' link.">
@@ -689,6 +692,11 @@
         </message>
       </if>
 
+      <!-- settings reset bubble messages -->
+      <message name="IDS_REPORT_BUBBLE_TEXT" desc="Text for the settings reset bubble reporting checkbox.">
+        Help make Google Chrome better by reporting the current settings
+      </message>
+
       <!-- chrome://settings/extensions page -->
       <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chrome cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode.">
         <ph name="BEGIN_BOLD">&lt;b&gt;</ph>Warning:<ph name="END_BOLD">&lt;/b&gt;</ph> Google Chrome cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option.
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index a60cd0d..da337aae 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -73,4 +73,9 @@
   <message name="IDS_SETTINGS_DOWNLOADS_PROMPT_FOR_DOWNLOAD_LABEL" desc="Label for the checkbox which enables a prompt for the user to choose a download location for each download instead of using the default.">
     Ask where to save each file before downloading
   </message>
+
+  <!-- Internet Page -->
+  <message name="IDS_SETTINGS_INTERNET_PAGE_TITLE" desc="Name of the settings page which displays internet preferences.">
+    Internet Connection
+  </message>
 </grit-part>
diff --git a/chrome/browser/PRESUBMIT.py b/chrome/browser/PRESUBMIT.py
index 5b42758..72303241 100644
--- a/chrome/browser/PRESUBMIT.py
+++ b/chrome/browser/PRESUBMIT.py
@@ -4,7 +4,7 @@
 
 """Presubmit script for Chromium browser code.
 
-This script currently only checks HTML/CSS/JS files in resources/.
+This script currently checks HTML/CSS/JS files in resources/ and ui/webui/.
 
 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
 for more details about the presubmit API built into depot_tools, and see
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 949db24..48d8576 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -21,7 +21,9 @@
 #include "chrome/browser/flags_storage.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "chrome/grit/google_chrome_strings.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
 #include "components/metrics/metrics_hashes.h"
@@ -1939,7 +1941,7 @@
     "answers-in-suggest",
     IDS_FLAGS_ENABLE_ANSWERS_IN_SUGGEST_NAME,
     IDS_FLAGS_ENABLE_ANSWERS_IN_SUGGEST_DESCRIPTION,
-    kOsAndroid | kOsWin | kOsLinux | kOsCrOS,
+    kOsAndroid | kOsDesktop,
     ENABLE_DISABLE_VALUE_TYPE(switches::kEnableAnswersInSuggest,
                               switches::kDisableAnswersInSuggest)
   },
diff --git a/chrome/browser/after_startup_task_utils.cc b/chrome/browser/after_startup_task_utils.cc
new file mode 100644
index 0000000..a4dc5ef
--- /dev/null
+++ b/chrome/browser/after_startup_task_utils.cc
@@ -0,0 +1,223 @@
+// 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 "chrome/browser/after_startup_task_utils.h"
+
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/process/process_info.h"
+#include "base/rand_util.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/task_runner.h"
+#include "base/tracked_objects.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_iterator.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebContentsObserver;
+using StartupCompleteFlag = base::CancellationFlag;
+
+namespace {
+
+struct AfterStartupTask {
+  AfterStartupTask(const tracked_objects::Location& from_here,
+                   const scoped_refptr<base::TaskRunner>& task_runner,
+                   const base::Closure& task)
+      : from_here(from_here), task_runner(task_runner), task(task) {}
+  ~AfterStartupTask() {}
+
+  const tracked_objects::Location from_here;
+  const scoped_refptr<base::TaskRunner> task_runner;
+  const base::Closure task;
+};
+
+// The flag may be read on any thread, but must only be set on the UI thread.
+base::LazyInstance<StartupCompleteFlag>::Leaky g_startup_complete_flag;
+
+// The queue may only be accessed on the UI thread.
+base::LazyInstance<std::deque<AfterStartupTask*>>::Leaky g_after_startup_tasks;
+
+bool IsBrowserStartupComplete() {
+  // Be sure to initialize the LazyInstance on the main thread since the flag
+  // may only be set on it's initializing thread.
+  if (g_startup_complete_flag == nullptr)
+    return false;
+  return g_startup_complete_flag.Get().IsSet();
+}
+
+void RunTask(scoped_ptr<AfterStartupTask> queued_task) {
+  // We're careful to delete the caller's |task| on the target runner's thread.
+  DCHECK(queued_task->task_runner->RunsTasksOnCurrentThread());
+  queued_task->task.Run();
+}
+
+void ScheduleTask(scoped_ptr<AfterStartupTask> queued_task) {
+  // Spread their execution over a brief time.
+  const int kMinDelaySec = 0;
+  const int kMaxDelaySec = 10;
+  scoped_refptr<base::TaskRunner> target_runner = queued_task->task_runner;
+  tracked_objects::Location from_here = queued_task->from_here;
+  target_runner->PostDelayedTask(
+      from_here, base::Bind(&RunTask, base::Passed(queued_task.Pass())),
+      base::TimeDelta::FromSeconds(base::RandInt(kMinDelaySec, kMaxDelaySec)));
+}
+
+void QueueTask(scoped_ptr<AfterStartupTask> queued_task) {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(QueueTask, base::Passed(queued_task.Pass())));
+    return;
+  }
+
+  // The flag may have been set while the task to invoke this method
+  // on the UI thread was inflight.
+  if (IsBrowserStartupComplete()) {
+    ScheduleTask(queued_task.Pass());
+    return;
+  }
+  g_after_startup_tasks.Get().push_back(queued_task.release());
+}
+
+void SetBrowserStartupIsComplete() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
+  // CurrentProcessInfo::CreationTime() is not available on all platforms.
+  const base::Time process_creation_time =
+      base::CurrentProcessInfo::CreationTime();
+  if (!process_creation_time.is_null()) {
+    UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
+                             base::Time::Now() - process_creation_time);
+  }
+#endif  // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
+  UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
+                             g_after_startup_tasks.Get().size());
+  g_startup_complete_flag.Get().Set();
+  for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
+    ScheduleTask(make_scoped_ptr(queued_task));
+  g_after_startup_tasks.Get().clear();
+
+  // The shrink_to_fit() method is not available for all of our build targets.
+  std::deque<AfterStartupTask*>(g_after_startup_tasks.Get())
+      .swap(g_after_startup_tasks.Get());
+}
+
+// Observes the first visible page load and sets the startup complete
+// flag accordingly.
+class StartupObserver : public WebContentsObserver, public base::NonThreadSafe {
+ public:
+  StartupObserver() : weak_factory_(this) {}
+  ~StartupObserver() override { DCHECK(IsBrowserStartupComplete()); }
+
+  void Start();
+
+ private:
+  void OnStartupComplete() {
+    DCHECK(CalledOnValidThread());
+    SetBrowserStartupIsComplete();
+    delete this;
+  }
+
+  void OnFailsafeTimeout() { OnStartupComplete(); }
+
+  // WebContentsObserver overrides
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override {
+    if (!render_frame_host->GetParent())
+      OnStartupComplete();
+  }
+
+  void DidFailLoad(content::RenderFrameHost* render_frame_host,
+                   const GURL& validated_url,
+                   int error_code,
+                   const base::string16& error_description) override {
+    if (!render_frame_host->GetParent())
+      OnStartupComplete();
+  }
+
+  void WebContentsDestroyed() override { OnStartupComplete(); }
+
+  base::WeakPtrFactory<StartupObserver> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(StartupObserver);
+};
+
+void StartupObserver::Start() {
+  // Signal completion quickly when there is no first page to load.
+  const int kShortDelaySecs = 3;
+  base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
+
+#if !defined(OS_ANDROID)
+  WebContents* contents = nullptr;
+  for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
+    contents = (*iter)->tab_strip_model()->GetActiveWebContents();
+    if (contents && contents->GetMainFrame() &&
+        contents->GetMainFrame()->GetVisibilityState() ==
+            blink::WebPageVisibilityStateVisible) {
+      break;
+    }
+  }
+
+  if (contents) {
+    // Give the page time to finish loading.
+    const int kLongerDelayMins = 3;
+    Observe(contents);
+    delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
+  }
+#else
+  // TODO(michaeln): We should probably monitor the initial page load here too,
+  // but since ChromeBrowserMainExtraPartsMetrics doesn't, not doing that yet.
+  const int kAndroidDelaySecs = 10;
+  delay = base::TimeDelta::FromSeconds(kAndroidDelaySecs);
+#endif  // !defined(OS_ANDROID)
+
+  BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
+                                 base::Bind(&StartupObserver::OnFailsafeTimeout,
+                                            weak_factory_.GetWeakPtr()),
+                                 delay);
+}
+
+}  // namespace
+
+void AfterStartupTaskUtils::StartMonitoringStartup() {
+  // The observer is self-deleting.
+  (new StartupObserver)->Start();
+}
+
+void AfterStartupTaskUtils::PostTask(
+    const tracked_objects::Location& from_here,
+    const scoped_refptr<base::TaskRunner>& task_runner,
+    const base::Closure& task) {
+  if (IsBrowserStartupComplete()) {
+    task_runner->PostTask(from_here, task);
+    return;
+  }
+
+  scoped_ptr<AfterStartupTask> queued_task(
+      new AfterStartupTask(from_here, task_runner, task));
+  QueueTask(queued_task.Pass());
+}
+
+void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
+  ::SetBrowserStartupIsComplete();
+}
+
+bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
+  return ::IsBrowserStartupComplete();
+}
+
+void AfterStartupTaskUtils::UnsafeResetForTesting() {
+  DCHECK(g_after_startup_tasks.Get().empty());
+  if (!IsBrowserStartupComplete())
+    return;
+  g_startup_complete_flag.Get().UnsafeResetForTesting();
+  DCHECK(!IsBrowserStartupComplete());
+}
diff --git a/chrome/browser/after_startup_task_utils.h b/chrome/browser/after_startup_task_utils.h
new file mode 100644
index 0000000..6c56fd3
--- /dev/null
+++ b/chrome/browser/after_startup_task_utils.h
@@ -0,0 +1,44 @@
+// 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 CHROME_BROWSER_AFTER_STARTUP_TASK_UTILS_H_
+#define CHROME_BROWSER_AFTER_STARTUP_TASK_UTILS_H_
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class TaskRunner;
+}
+namespace tracked_objects {
+class Location;
+};
+
+class AfterStartupTaskUtils {
+ public:
+  // Observes startup and when complete runs tasks that have accrued.
+  static void StartMonitoringStartup();
+
+  // Used to augment the behavior of BrowserThread::PostAfterStartupTask
+  // for chrome. Tasks are queued until startup is complete.
+  // Note: see browser_thread.h
+  static void PostTask(const tracked_objects::Location& from_here,
+                       const scoped_refptr<base::TaskRunner>& task_runner,
+                       const base::Closure& task);
+
+ private:
+  friend class AfterStartupTaskTest;
+  FRIEND_TEST_ALL_PREFIXES(AfterStartupTaskTest, IsStartupComplete);
+  FRIEND_TEST_ALL_PREFIXES(AfterStartupTaskTest, PostTask);
+
+  static bool IsBrowserStartupComplete();
+  static void SetBrowserStartupIsComplete();
+  static void UnsafeResetForTesting();
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AfterStartupTaskUtils);
+};
+
+#endif  // CHROME_BROWSER_AFTER_STARTUP_TASK_UTILS_H_
diff --git a/chrome/browser/after_startup_task_utils_unittest.cc b/chrome/browser/after_startup_task_utils_unittest.cc
new file mode 100644
index 0000000..bc6971d
--- /dev/null
+++ b/chrome/browser/after_startup_task_utils_unittest.cc
@@ -0,0 +1,199 @@
+// 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 "chrome/browser/after_startup_task_utils.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::RunLoop;
+using content::BrowserThread;
+using content::TestBrowserThreadBundle;
+
+namespace {
+
+class WrappedTaskRunner : public base::TaskRunner {
+ public:
+  explicit WrappedTaskRunner(const scoped_refptr<TaskRunner>& real_runner)
+      : real_task_runner_(real_runner) {}
+
+  bool PostDelayedTask(const tracked_objects::Location& from_here,
+                       const base::Closure& task,
+                       base::TimeDelta delay) override {
+    ++posted_task_count_;
+    return real_task_runner_->PostDelayedTask(
+        from_here, base::Bind(&WrappedTaskRunner::RunWrappedTask, this, task),
+        base::TimeDelta());  // Squash all delays so our tests complete asap.
+  }
+
+  bool RunsTasksOnCurrentThread() const override {
+    return real_task_runner_->RunsTasksOnCurrentThread();
+  }
+
+  base::TaskRunner* real_runner() const { return real_task_runner_.get(); }
+
+  int total_task_count() const { return posted_task_count_ + ran_task_count_; }
+  int posted_task_count() const { return posted_task_count_; }
+  int ran_task_count() const { return ran_task_count_; }
+
+  void reset_task_counts() {
+    posted_task_count_ = 0;
+    ran_task_count_ = 0;
+  }
+
+ private:
+  ~WrappedTaskRunner() override {}
+
+  void RunWrappedTask(const base::Closure& task) {
+    ++ran_task_count_;
+    task.Run();
+  }
+
+  scoped_refptr<TaskRunner> real_task_runner_;
+  int posted_task_count_ = 0;
+  int ran_task_count_ = 0;
+};
+
+}  // namespace
+
+class AfterStartupTaskTest : public testing::Test {
+ public:
+  AfterStartupTaskTest()
+      : browser_thread_bundle_(TestBrowserThreadBundle::REAL_DB_THREAD) {
+    ui_thread_ = new WrappedTaskRunner(
+        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
+    db_thread_ = new WrappedTaskRunner(
+        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB));
+    AfterStartupTaskUtils::UnsafeResetForTesting();
+  }
+
+  // Hop to the db thread and call IsBrowserStartupComplete.
+  bool GetIsBrowserStartupCompleteFromDBThread() {
+    RunLoop run_loop;
+    bool is_complete;
+    base::PostTaskAndReplyWithResult(
+        db_thread_->real_runner(), FROM_HERE,
+        base::Bind(&AfterStartupTaskUtils::IsBrowserStartupComplete),
+        base::Bind(&AfterStartupTaskTest::GotIsOnBrowserStartupComplete,
+                   &run_loop, &is_complete));
+    run_loop.Run();
+    return is_complete;
+  }
+
+  // Hop to the db thread and call PostAfterStartupTask.
+  void PostAfterStartupTaskFromDBThread(
+      const tracked_objects::Location& from_here,
+      const scoped_refptr<base::TaskRunner>& task_runner,
+      const base::Closure& task) {
+    RunLoop run_loop;
+    db_thread_->real_runner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&AfterStartupTaskUtils::PostTask, from_here,
+                              task_runner, task),
+        base::Bind(&RunLoop::Quit, base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  // Make sure all tasks posted to the DB thread get run.
+  void FlushDBThread() {
+    RunLoop run_loop;
+    db_thread_->real_runner()->PostTaskAndReply(
+        FROM_HERE, base::Bind(&base::DoNothing),
+        base::Bind(&RunLoop::Quit, base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+
+  static void VerifyExpectedThread(BrowserThread::ID id) {
+    EXPECT_TRUE(BrowserThread::CurrentlyOn(id));
+  }
+
+ protected:
+  scoped_refptr<WrappedTaskRunner> ui_thread_;
+  scoped_refptr<WrappedTaskRunner> db_thread_;
+
+ private:
+  static void GotIsOnBrowserStartupComplete(RunLoop* loop,
+                                            bool* out,
+                                            bool is_complete) {
+    *out = is_complete;
+    loop->Quit();
+  }
+
+  TestBrowserThreadBundle browser_thread_bundle_;
+};
+
+TEST_F(AfterStartupTaskTest, IsStartupComplete) {
+  // Check IsBrowserStartupComplete on a background thread first to
+  // verify that it does not allocate the underlying flag on that thread.
+  // That allocation thread correctness part of this test relies on
+  // the DCHECK in CancellationFlag::Set().
+  EXPECT_FALSE(GetIsBrowserStartupCompleteFromDBThread());
+  EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
+  AfterStartupTaskUtils::SetBrowserStartupIsComplete();
+  EXPECT_TRUE(AfterStartupTaskUtils::IsBrowserStartupComplete());
+  EXPECT_TRUE(GetIsBrowserStartupCompleteFromDBThread());
+}
+
+TEST_F(AfterStartupTaskTest, PostTask) {
+  // Nothing should be posted prior to startup completion.
+  EXPECT_FALSE(AfterStartupTaskUtils::IsBrowserStartupComplete());
+  AfterStartupTaskUtils::PostTask(
+      FROM_HERE, ui_thread_,
+      base::Bind(&AfterStartupTaskTest::VerifyExpectedThread,
+                 BrowserThread::UI));
+  AfterStartupTaskUtils::PostTask(
+      FROM_HERE, db_thread_,
+      base::Bind(&AfterStartupTaskTest::VerifyExpectedThread,
+                 BrowserThread::DB));
+  PostAfterStartupTaskFromDBThread(
+      FROM_HERE, ui_thread_,
+      base::Bind(&AfterStartupTaskTest::VerifyExpectedThread,
+                 BrowserThread::UI));
+  PostAfterStartupTaskFromDBThread(
+      FROM_HERE, db_thread_,
+      base::Bind(&AfterStartupTaskTest::VerifyExpectedThread,
+                 BrowserThread::DB));
+  RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, db_thread_->total_task_count() + ui_thread_->total_task_count());
+
+  // Queued tasks should be posted upon setting the flag.
+  AfterStartupTaskUtils::SetBrowserStartupIsComplete();
+  EXPECT_EQ(2, db_thread_->posted_task_count());
+  EXPECT_EQ(2, ui_thread_->posted_task_count());
+  FlushDBThread();
+  RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, db_thread_->ran_task_count());
+  EXPECT_EQ(2, ui_thread_->ran_task_count());
+
+  db_thread_->reset_task_counts();
+  ui_thread_->reset_task_counts();
+  EXPECT_EQ(0, db_thread_->total_task_count() + ui_thread_->total_task_count());
+
+  // Tasks posted after startup should get posted immediately.
+  AfterStartupTaskUtils::PostTask(FROM_HERE, ui_thread_,
+                                  base::Bind(&base::DoNothing));
+  AfterStartupTaskUtils::PostTask(FROM_HERE, db_thread_,
+                                  base::Bind(&base::DoNothing));
+  EXPECT_EQ(1, db_thread_->posted_task_count());
+  EXPECT_EQ(1, ui_thread_->posted_task_count());
+  PostAfterStartupTaskFromDBThread(FROM_HERE, ui_thread_,
+                                   base::Bind(&base::DoNothing));
+  PostAfterStartupTaskFromDBThread(FROM_HERE, db_thread_,
+                                   base::Bind(&base::DoNothing));
+  EXPECT_EQ(2, db_thread_->posted_task_count());
+  EXPECT_EQ(2, ui_thread_->posted_task_count());
+  FlushDBThread();
+  RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, db_thread_->ran_task_count());
+  EXPECT_EQ(2, ui_thread_->ran_task_count());
+}
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 5172935..750604c 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/prerender/prerender_link_manager.h"
 #include "chrome/browser/prerender/prerender_link_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -712,6 +713,38 @@
     return guest_web_contents;
   }
 
+  // Helper to load interstitial page in a <webview>.
+  void InterstitialTeardownTestHelper() {
+    // Start a HTTPS server so we can load an interstitial page inside guest.
+    net::SpawnedTestServer::SSLOptions ssl_options;
+    ssl_options.server_certificate =
+        net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
+    net::SpawnedTestServer https_server(
+        net::SpawnedTestServer::TYPE_HTTPS, ssl_options,
+        base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+    ASSERT_TRUE(https_server.Start());
+
+    net::HostPortPair host_and_port = https_server.host_port_pair();
+
+    LoadAndLaunchPlatformApp("web_view/interstitial_teardown",
+                             "EmbedderLoaded");
+
+    // Now load the guest.
+    content::WebContents* embedder_web_contents =
+        GetFirstAppWindowWebContents();
+    ExtensionTestMessageListener second("GuestAddedToDom", false);
+    EXPECT_TRUE(content::ExecuteScript(
+        embedder_web_contents,
+        base::StringPrintf("loadGuest(%d);\n", host_and_port.port())));
+    ASSERT_TRUE(second.WaitUntilSatisfied());
+
+    // Wait for interstitial page to be shown in guest.
+    content::WebContents* guest_web_contents =
+        GetGuestViewManager()->WaitForSingleGuestCreated();
+    ASSERT_TRUE(guest_web_contents->GetRenderProcessHost()->IsIsolatedGuest());
+    content::WaitForInterstitialAttach(guest_web_contents);
+  }
+
   // Runs media_access/allow tests.
   void MediaAccessAPIAllowTestHelper(const std::string& test_name);
 
@@ -1337,8 +1370,8 @@
              NO_TEST_SERVER);
 }
 
-// This test makes sure we do not crash if app is closed while interstitial
-// page is being shown in guest.
+// This test makes sure the browser process does not crash if app is closed
+// while an interstitial page is being shown in guest.
 IN_PROC_BROWSER_TEST_F(WebViewTest, InterstitialTeardown) {
 #if defined(OS_WIN)
   // Flaky on XP bot http://crbug.com/297014
@@ -1346,38 +1379,35 @@
     return;
 #endif
 
-  // Start a HTTPS server so we can load an interstitial page inside guest.
-  net::SpawnedTestServer::SSLOptions ssl_options;
-  ssl_options.server_certificate =
-      net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
-  net::SpawnedTestServer https_server(
-      net::SpawnedTestServer::TYPE_HTTPS, ssl_options,
-      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
-  ASSERT_TRUE(https_server.Start());
-
-  net::HostPortPair host_and_port = https_server.host_port_pair();
-
-  LoadAndLaunchPlatformApp("web_view/interstitial_teardown", "EmbedderLoaded");
-
-  // Now load the guest.
-  content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
-  ExtensionTestMessageListener second("GuestAddedToDom", false);
-  EXPECT_TRUE(content::ExecuteScript(
-      embedder_web_contents,
-      base::StringPrintf("loadGuest(%d);\n", host_and_port.port())));
-  ASSERT_TRUE(second.WaitUntilSatisfied());
-
-  // Wait for interstitial page to be shown in guest.
-  content::WebContents* guest_web_contents =
-      GetGuestViewManager()->WaitForSingleGuestCreated();
-  ASSERT_TRUE(guest_web_contents->GetRenderProcessHost()->IsIsolatedGuest());
-  content::WaitForInterstitialAttach(guest_web_contents);
+  InterstitialTeardownTestHelper();
 
   // Now close the app while interstitial page being shown in guest.
   extensions::AppWindow* window = GetFirstAppWindow();
   window->GetBaseWindow()->Close();
 }
 
+// This test makes sure the browser process does not crash if browser is shut
+// down while an interstitial page is being shown in guest.
+IN_PROC_BROWSER_TEST_F(WebViewTest, InterstitialTeardownOnBrowserShutdown) {
+#if defined(OS_WIN)
+  // http://crbug.com/297014
+  if (base::win::GetVersion() <= base::win::VERSION_XP)
+    return;
+#endif
+
+  InterstitialTeardownTestHelper();
+
+  // Now close the app while interstitial page being shown in guest.
+  extensions::AppWindow* window = GetFirstAppWindow();
+  window->GetBaseWindow()->Close();
+
+  // InterstitialPage is not destroyed immediately, so the
+  // RenderWidgetHostViewGuest for it is still there, closing all
+  // renderer processes will cause the RWHVGuest's RenderProcessGone()
+  // shutdown path to be exercised.
+  chrome::CloseAllBrowsers();
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, ShimSrcAttribute) {
   ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/src_attribute"))
       << message_;
diff --git a/chrome/browser/bookmarks/enhanced_bookmarks_features.cc b/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
index 8f69f07..bbe6335 100644
--- a/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
+++ b/chrome/browser/bookmarks/enhanced_bookmarks_features.cc
@@ -14,10 +14,6 @@
 #include "extensions/common/features/feature_provider.h"
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
 
-#if defined(OS_ANDROID)
-#include "base/android/build_info.h"
-#endif  // defined(OS_ANDROID)
-
 namespace {
 
 const char kFieldTrialName[] = "EnhancedBookmarks";
@@ -75,10 +71,6 @@
 
   bool opt_out = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
                      switches::kEnhancedBookmarksExperiment) == "0";
-#if defined(OS_ANDROID)
-  opt_out |= base::android::BuildInfo::GetInstance()->sdk_int() <
-                 base::android::SdkVersion::SDK_VERSION_ICE_CREAM_SANDWICH_MR1;
-#endif  // defined(OS_ANDROID)
 
   if (opt_out)
     return false;
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index b373810..254d2ef9 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -152,7 +152,7 @@
 static const int kUpdateCheckIntervalHours = 6;
 #endif
 
-#if defined(USE_X11) || defined(OS_WIN)
+#if defined(USE_X11) || defined(OS_WIN) || defined(USE_OZONE)
 // How long to wait for the File thread to complete during EndSession, on Linux
 // and Windows. We have a timeout here because we're unable to run the UI
 // messageloop and there's some deadlock risk. Our only option is to exit
@@ -498,7 +498,7 @@
   //
   // If you change the condition here, be sure to also change
   // ProfileBrowserTests to match.
-#if defined(USE_X11) || defined(OS_WIN)
+#if defined(USE_X11) || defined(OS_WIN) || defined(USE_OZONE)
   // Do a best-effort wait on the successful countdown of rundown tasks. Note
   // that if we don't complete "quickly enough", Windows will terminate our
   // process.
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 7d3a50a..f42f31f8 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -39,6 +39,7 @@
 #include "build/build_config.h"
 #include "cc/base/switches.h"
 #include "chrome/browser/about_flags.h"
+#include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -1127,6 +1128,13 @@
       base::Bind(&WebRtcLogUtil::DeleteOldWebRtcLogFilesForAllProfiles),
       base::TimeDelta::FromMinutes(1));
 #endif  // defined(ENABLE_WEBRTC)
+
+  // At this point, StartupBrowserCreator::Start has run creating initial
+  // browser windows and tabs, but no progress has been made in loading
+  // content as the main message loop hasn't started processing tasks yet.
+  // We setup to observe to the initial page load here to defer running
+  // task posted via PostAfterStartupTask until its complete.
+  AfterStartupTaskUtils::StartMonitoringStartup();
 }
 
 int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1c16189..2617765 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -20,6 +20,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/browser_about_handler.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_shutdown.h"
@@ -720,6 +721,13 @@
   return main_parts;
 }
 
+void ChromeContentBrowserClient::PostAfterStartupTask(
+    const tracked_objects::Location& from_here,
+    const scoped_refptr<base::TaskRunner>& task_runner,
+    const base::Closure& task) {
+  AfterStartupTaskUtils::PostTask(from_here, task_runner, task);
+}
+
 std::string ChromeContentBrowserClient::GetStoragePartitionIdForSite(
     content::BrowserContext* browser_context,
     const GURL& site) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 6af499c3..2f23b384 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -50,6 +50,9 @@
 
   content::BrowserMainParts* CreateBrowserMainParts(
       const content::MainFunctionParams& parameters) override;
+  void PostAfterStartupTask(const tracked_objects::Location& from_here,
+                            const scoped_refptr<base::TaskRunner>& task_runner,
+                            const base::Closure& task) override;
   std::string GetStoragePartitionIdForSite(
       content::BrowserContext* browser_context,
       const GURL& site) override;
@@ -241,9 +244,8 @@
       content::FileDescriptorInfo* mappings) override;
 #endif
 #if defined(OS_WIN)
-  virtual const wchar_t* GetResourceDllName() override;
-  virtual void PreSpawnRenderer(sandbox::TargetPolicy* policy,
-                                bool* success) override;
+  const wchar_t* GetResourceDllName() override;
+  void PreSpawnRenderer(sandbox::TargetPolicy* policy, bool* success) override;
 #endif
   bool CheckMediaAccessPermission(content::BrowserContext* browser_context,
                                   const GURL& security_origin,
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index 96f88bd..17f1443 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/stringprintf.h"
@@ -11,6 +12,7 @@
 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
 #include "chrome/common/url_constants.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/cros_disks_client.h"
 #include "storage/browser/fileapi/async_file_util.h"
 #include "storage/browser/fileapi/external_mount_points.h"
@@ -243,6 +245,13 @@
   if (type == storage::kFileSystemTypeProvided)
     return file_system_provider_delegate_->GetWatcherManager(type);
 
+  // Enables MTP file watcher only when MTP write support is enabled.
+  if (type == storage::kFileSystemTypeDeviceMediaAsFileStorage &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kEnableMtpWriteSupport)) {
+    return mtp_delegate_->GetWatcherManager(type);
+  }
+
   // TODO(mtomasz): Add support for other backends.
   return NULL;
 }
diff --git a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc
index 39a4955..e0b88eb 100644
--- a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc
+++ b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc
@@ -14,7 +14,9 @@
     const base::FilePath& storage_partition_path)
     : device_media_async_file_util_(
           DeviceMediaAsyncFileUtil::Create(storage_partition_path,
-                                           NO_MEDIA_FILE_VALIDATION)) {
+                                           NO_MEDIA_FILE_VALIDATION)),
+      mtp_watcher_manager_(
+          new MTPWatcherManager(device_media_async_file_util_.get())) {
 }
 
 MTPFileSystemBackendDelegate::~MTPFileSystemBackendDelegate() {
@@ -53,8 +55,8 @@
 
 storage::WatcherManager* MTPFileSystemBackendDelegate::GetWatcherManager(
     storage::FileSystemType type) {
-  NOTIMPLEMENTED();
-  return NULL;
+  DCHECK_EQ(storage::kFileSystemTypeDeviceMediaAsFileStorage, type);
+  return mtp_watcher_manager_.get();
 }
 
 void MTPFileSystemBackendDelegate::GetRedirectURLForContents(
diff --git a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h
index e49add0..6a6d3f34 100644
--- a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h
+++ b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
+#include "chrome/browser/chromeos/fileapi/mtp_watcher_manager.h"
 
 namespace base {
 class FilePath;
@@ -53,6 +54,7 @@
 
  private:
   scoped_ptr<DeviceMediaAsyncFileUtil> device_media_async_file_util_;
+  scoped_ptr<MTPWatcherManager> mtp_watcher_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(MTPFileSystemBackendDelegate);
 };
diff --git a/chrome/browser/chromeos/fileapi/mtp_watcher_manager.cc b/chrome/browser/chromeos/fileapi/mtp_watcher_manager.cc
new file mode 100644
index 0000000..58439f8e
--- /dev/null
+++ b/chrome/browser/chromeos/fileapi/mtp_watcher_manager.cc
@@ -0,0 +1,33 @@
+// 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 "chrome/browser/chromeos/fileapi/mtp_watcher_manager.h"
+
+namespace chromeos {
+
+MTPWatcherManager::MTPWatcherManager(
+    DeviceMediaAsyncFileUtil* device_media_async_file_util)
+    : device_media_async_file_util_(device_media_async_file_util) {
+  DCHECK(device_media_async_file_util != NULL);
+}
+
+MTPWatcherManager::~MTPWatcherManager() {
+}
+
+void MTPWatcherManager::AddWatcher(
+    const storage::FileSystemURL& url,
+    bool recursive,
+    const StatusCallback& callback,
+    const NotificationCallback& notification_callback) {
+  device_media_async_file_util_->AddWatcher(url, recursive, callback,
+                                            notification_callback);
+}
+
+void MTPWatcherManager::RemoveWatcher(const storage::FileSystemURL& url,
+                                      bool recursive,
+                                      const StatusCallback& callback) {
+  device_media_async_file_util_->RemoveWatcher(url, recursive, callback);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/fileapi/mtp_watcher_manager.h b/chrome/browser/chromeos/fileapi/mtp_watcher_manager.h
new file mode 100644
index 0000000..613d5ee
--- /dev/null
+++ b/chrome/browser/chromeos/fileapi/mtp_watcher_manager.h
@@ -0,0 +1,40 @@
+// 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 CHROME_BROWSER_CHROMEOS_FILEAPI_MTP_WATCHER_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_FILEAPI_MTP_WATCHER_MANAGER_H_
+
+#include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h"
+#include "storage/browser/fileapi/watcher_manager.h"
+
+namespace storage {
+
+class FileSystemURL;
+
+}  // namespace storage
+
+namespace chromeos {
+
+class MTPWatcherManager : public storage::WatcherManager {
+ public:
+  explicit MTPWatcherManager(
+      DeviceMediaAsyncFileUtil* device_media_async_file_util);
+  ~MTPWatcherManager() override;
+
+  void AddWatcher(const storage::FileSystemURL& url,
+                  bool recursive,
+                  const StatusCallback& callback,
+                  const NotificationCallback& notification_callback) override;
+
+  void RemoveWatcher(const storage::FileSystemURL& url,
+                     bool recursive,
+                     const StatusCallback& callback) override;
+
+ private:
+  DeviceMediaAsyncFileUtil* const device_media_async_file_util_;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_FILEAPI_MTP_WATCHER_MANAGER_H_
diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator.cc b/chrome/browser/component_updater/chrome_component_updater_configurator.cc
index 36f602d..f4019220 100644
--- a/chrome/browser/component_updater/chrome_component_updater_configurator.cc
+++ b/chrome/browser/component_updater/chrome_component_updater_configurator.cc
@@ -17,6 +17,7 @@
 #endif  // OS_WIN
 #include "build/build_config.h"
 #include "chrome/browser/component_updater/component_patcher_operation_out_of_process.h"
+#include "chrome/browser/component_updater/url_constants.h"
 #include "chrome/browser/update_client/chrome_update_query_params_delegate.h"
 #include "chrome/common/chrome_version_info.h"
 #include "components/component_updater/component_updater_switches.h"
@@ -50,17 +51,6 @@
 // Sets the URL for updates.
 const char kSwitchUrlSource[] = "url-source";
 
-#define COMPONENT_UPDATER_SERVICE_ENDPOINT \
-  "//clients2.google.com/service/update2"
-
-// The default URL for the v3 protocol service endpoint. In some cases, the
-// component updater is allowed to fall back to and alternate URL source, if
-// the request to the default URL source fails.
-// The value of |kDefaultUrlSource| can be overridden with
-// --component-updater=url-source=someurl.
-const char kDefaultUrlSource[] = "https:" COMPONENT_UPDATER_SERVICE_ENDPOINT;
-const char kAltUrlSource[] = "http:" COMPONENT_UPDATER_SERVICE_ENDPOINT;
-
 // Disables differential updates.
 const char kSwitchDisableDeltaUpdates[] = "disable-delta-updates";
 
@@ -218,9 +208,9 @@
   if (url_source_override_.is_valid()) {
     urls.push_back(GURL(url_source_override_));
   } else {
-    urls.push_back(GURL(kDefaultUrlSource));
+    urls.push_back(GURL(kUpdaterDefaultUrl));
     if (fallback_to_alt_source_url_enabled_) {
-      urls.push_back(GURL(kAltUrlSource));
+      urls.push_back(GURL(kUpdaterAltUrl));
     }
   }
   return urls;
diff --git a/chrome/browser/component_updater/url_constants.cc b/chrome/browser/component_updater/url_constants.cc
new file mode 100644
index 0000000..a27f21a
--- /dev/null
+++ b/chrome/browser/component_updater/url_constants.cc
@@ -0,0 +1,19 @@
+// 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 "chrome/browser/component_updater/url_constants.h"
+
+namespace component_updater {
+
+// The alternative URL for the v3 protocol service endpoint.
+const char kUpdaterAltUrl[] = "http://clients2.google.com/service/update2";
+
+// The default URL for the v3 protocol service endpoint. In some cases, the
+// component updater is allowed to fall back to and alternate URL source, if
+// the request to the default URL source fails.
+// The value of |kDefaultUrlSource| can be overridden with
+// --component-updater=url-source=someurl.
+const char kUpdaterDefaultUrl[] = "https://clients2.google.com/service/update2";
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/url_constants.h b/chrome/browser/component_updater/url_constants.h
new file mode 100644
index 0000000..1c00d70
--- /dev/null
+++ b/chrome/browser/component_updater/url_constants.h
@@ -0,0 +1,15 @@
+// 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 CHROME_BROWSER_COMPONENT_UPDATER_URL_CONSTANTS_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_URL_CONSTANTS_H_
+
+namespace component_updater {
+
+extern const char kUpdaterAltUrl[];
+extern const char kUpdaterDefaultUrl[];
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_URL_CONSTANTS_H_
diff --git a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
index 934e3f5..e502981 100644
--- a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
+++ b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
@@ -802,4 +802,91 @@
   normal_provider.ShutdownOnUIThread();
 }
 
+TEST(PrefProviderTest, ClearAllContentSettingsRules) {
+  TestingPrefServiceSyncable prefs;
+  PrefProvider::RegisterProfilePrefs(prefs.registry());
+
+  ContentSettingsPattern pattern =
+      ContentSettingsPattern::FromString("google.com");
+  ContentSettingsPattern wildcard =
+      ContentSettingsPattern::FromString("*");
+  scoped_ptr<base::Value> value(
+      new base::FundamentalValue(CONTENT_SETTING_ALLOW));
+  ResourceIdentifier res_id("abcde");
+
+  PrefProvider provider(&prefs, false);
+
+  // Non-empty pattern, syncable, empty resource identifier.
+  provider.SetWebsiteSetting(pattern, wildcard, CONTENT_SETTINGS_TYPE_IMAGES,
+                             ResourceIdentifier(), value->DeepCopy());
+
+  // Non-empty pattern, non-syncable, empty resource identifier.
+  provider.SetWebsiteSetting(pattern, wildcard,
+                             CONTENT_SETTINGS_TYPE_GEOLOCATION,
+                             ResourceIdentifier(), value->DeepCopy());
+
+  // Non-empty pattern, plugins, non-empty resource identifier.
+  provider.SetWebsiteSetting(pattern, wildcard, CONTENT_SETTINGS_TYPE_PLUGINS,
+                             res_id, value->DeepCopy());
+
+  // Empty pattern, plugins, non-empty resource identifier.
+  provider.SetWebsiteSetting(wildcard, wildcard, CONTENT_SETTINGS_TYPE_PLUGINS,
+                             res_id, value->DeepCopy());
+
+  // Non-empty pattern, syncable, empty resource identifier.
+  provider.SetWebsiteSetting(pattern, wildcard, CONTENT_SETTINGS_TYPE_COOKIES,
+                             ResourceIdentifier(), value->DeepCopy());
+
+  // Non-empty pattern, non-syncable, empty resource identifier.
+  provider.SetWebsiteSetting(pattern, wildcard,
+                             CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+                             ResourceIdentifier(), value->DeepCopy());
+
+  provider.ClearAllContentSettingsRules(CONTENT_SETTINGS_TYPE_IMAGES);
+  provider.ClearAllContentSettingsRules(CONTENT_SETTINGS_TYPE_GEOLOCATION);
+  provider.ClearAllContentSettingsRules(CONTENT_SETTINGS_TYPE_PLUGINS);
+
+  // Test that the new preferences for images, geolocation and plugins
+  // are empty.
+  const char* empty_prefs[] = {
+      prefs::kContentSettingsImagesPatternPairs,
+      prefs::kContentSettingsGeolocationPatternPairs,
+      prefs::kContentSettingsPluginsPatternPairs
+  };
+
+  for (const char* pref : empty_prefs) {
+    DictionaryPrefUpdate update(&prefs, pref);
+    const base::DictionaryValue* dictionary = update.Get();
+    EXPECT_TRUE(dictionary->empty());
+  }
+
+  // Test that the preferences for cookies and notifications are not empty.
+  const char* nonempty_prefs[] = {
+      prefs::kContentSettingsCookiesPatternPairs,
+      prefs::kContentSettingsNotificationsPatternPairs
+  };
+
+  for (const char* pref : nonempty_prefs) {
+    DictionaryPrefUpdate update(&prefs, pref);
+    const base::DictionaryValue* dictionary = update.Get();
+    EXPECT_EQ(1u, dictionary->size());
+  }
+
+  // Test that the old preference only contains cookies and notifications.
+  {
+    DictionaryPrefUpdate update(&prefs, prefs::kContentSettingsPatternPairs);
+    const base::DictionaryValue* dictionary = update.Get();
+    const base::DictionaryValue* exception;
+    EXPECT_TRUE(dictionary->GetDictionaryWithoutPathExpansion(
+        CreatePatternString(pattern, wildcard), &exception));
+    EXPECT_EQ(1u, exception->size());
+    EXPECT_TRUE(exception->HasKey(GetTypeName(CONTENT_SETTINGS_TYPE_COOKIES)));
+
+    // The notification setting was not cleared, but it was also never written
+    // to the old preference, as it is unsyncable.
+  }
+
+  provider.ShutdownOnUIThread();
+}
+
 }  // namespace content_settings
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index 75b13769..dc14204 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -708,14 +708,14 @@
   TestingProfile profile;
 
   scoped_ptr<base::Value> value(base::JSONReader::Read(
-      "{\"[*.]\\xC4\\x87ira.com,*\":{\"setting\":1}}"));
-  profile.GetPrefs()->Set(prefs::kContentSettingsImagesPatternPairs, *value);
+      "{\"[*.]\\xC4\\x87ira.com,*\":{\"images\":1}}"));
+  profile.GetPrefs()->Set(prefs::kContentSettingsPatternPairs, *value);
 
   // Set punycode equivalent, with different setting.
   scoped_ptr<base::Value> puny_value(base::JSONReader::Read(
-      "{\"[*.]xn--ira-ppa.com,*\":{\"setting\":2}}"));
+      "{\"[*.]xn--ira-ppa.com,*\":{\"images\":2}}"));
   profile.GetPrefs()->Set(
-      prefs::kContentSettingsImagesPatternPairs, *puny_value);
+      prefs::kContentSettingsPatternPairs, *puny_value);
 
   // Initialize the content map.
   profile.GetHostContentSettingsMap();
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 1529f26..81f354d 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -27,7 +27,9 @@
 void ChromeDevToolsManagerDelegate::Inspect(
     content::BrowserContext* browser_context,
     content::DevToolsAgentHost* agent_host) {
-  if (!agent_host->IsWorker()) {
+  content::DevToolsAgentHost::Type type = agent_host->GetType();
+  if (type != content::DevToolsAgentHost::TYPE_SHARED_WORKER &&
+      type != content::DevToolsAgentHost::TYPE_SERVICE_WORKER) {
     // TODO(horo): Support other types of DevToolsAgentHost when necessary.
     NOTREACHED() << "Inspect() only supports workers.";
   }
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
index 6d4358d..0534993 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -194,5 +194,13 @@
                      &Delegate::RecordEnumeratedHistogram, delegate);
   d->RegisterHandlerWithCallback("sendJsonRequest",
                                  &Delegate::SendJsonRequest, delegate);
+  d->RegisterHandlerWithCallback("getPreferences",
+                                 &Delegate::GetPreferences, delegate);
+  d->RegisterHandler("setPreference",
+                     &Delegate::SetPreference, delegate);
+  d->RegisterHandler("removePreference",
+                     &Delegate::RemovePreference, delegate);
+  d->RegisterHandler("clearPreferences",
+                     &Delegate::ClearPreferences, delegate);
   return d;
 }
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index 882bce6..badf46f 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -66,6 +66,11 @@
     virtual void ZoomOut() = 0;
     virtual void ResetZoom() = 0;
     virtual void SetDevicesUpdatesEnabled(bool enabled) = 0;
+    virtual void GetPreferences(const DispatchCallback& callback) = 0;
+    virtual void SetPreference(const std::string& name,
+                               const std::string& value) = 0;
+    virtual void RemovePreference(const std::string& name) = 0;
+    virtual void ClearPreferences() = 0;
     virtual void SendMessageToBrowser(const std::string& message) = 0;
     virtual void RecordEnumeratedHistogram(const std::string& name,
                                            int sample,
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 294b9c2..3f751d3 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -8,6 +8,7 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/metrics/histogram.h"
+#include "base/prefs/scoped_user_pref_update.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -17,6 +18,7 @@
 #include "chrome/browser/devtools/devtools_target_impl.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -28,6 +30,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
@@ -205,6 +208,8 @@
   void SetIsDocked(bool is_docked) override {}
   void OpenInNewTab(const std::string& url) override;
   void SetWhitelistedShortcuts(const std::string& message) override {}
+  using DispatchCallback =
+      DevToolsEmbedderMessageDispatcher::Delegate::DispatchCallback;
 
   void InspectedContentsClosing() override;
   void OnLoadCompleted() override {}
@@ -735,6 +740,31 @@
   }
 }
 
+void DevToolsUIBindings::GetPreferences(const DispatchCallback& callback) {
+  const DictionaryValue* prefs =
+      profile_->GetPrefs()->GetDictionary(prefs::kDevToolsPreferences);
+  callback.Run(prefs);
+}
+
+void DevToolsUIBindings::SetPreference(const std::string& name,
+                                   const std::string& value) {
+  DictionaryPrefUpdate update(profile_->GetPrefs(),
+                              prefs::kDevToolsPreferences);
+  update.Get()->SetStringWithoutPathExpansion(name, value);
+}
+
+void DevToolsUIBindings::RemovePreference(const std::string& name) {
+  DictionaryPrefUpdate update(profile_->GetPrefs(),
+                              prefs::kDevToolsPreferences);
+  update.Get()->RemoveWithoutPathExpansion(name, nullptr);
+}
+
+void DevToolsUIBindings::ClearPreferences() {
+  DictionaryPrefUpdate update(profile_->GetPrefs(),
+                              prefs::kDevToolsPreferences);
+  update.Get()->Clear();
+}
+
 void DevToolsUIBindings::SendMessageToBrowser(const std::string& message) {
   if (agent_host_.get())
     agent_host_->DispatchProtocolMessage(message);
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index 4efe4947..060ac32 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -138,6 +138,11 @@
   void SendJsonRequest(const DispatchCallback& callback,
                        const std::string& browser_id,
                        const std::string& url) override;
+  void GetPreferences(const DispatchCallback& callback) override;
+  void SetPreference(const std::string& name,
+                     const std::string& value) override;
+  void RemovePreference(const std::string& name) override;
+  void ClearPreferences() override;
 
   // net::URLFetcherDelegate overrides.
   void OnURLFetchComplete(const net::URLFetcher* source) override;
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 502feb0f..5df0546 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -329,6 +329,9 @@
   registry->RegisterDictionaryPref(
       prefs::kDevToolsPortForwardingConfig,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+  registry->RegisterDictionaryPref(
+      prefs::kDevToolsPreferences,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 }
 
 // static
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index dd89827..a03f0dd 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -207,4 +207,7 @@
   if (!use_ozone) {
     sources -= [ "global_shortcut_listener_ozone.cc" ]
   }
+  if (enable_media_router) {
+    defines += [ "ENABLE_MEDIA_ROUTER=1" ]
+  }
 }
diff --git a/chrome/browser/extensions/dev_mode_bubble_controller.cc b/chrome/browser/extensions/dev_mode_bubble_controller.cc
index d6914e5..202207c 100644
--- a/chrome/browser/extensions/dev_mode_bubble_controller.cc
+++ b/chrome/browser/extensions/dev_mode_bubble_controller.cc
@@ -52,7 +52,6 @@
   base::string16 GetActionButtonLabel() const override;
   base::string16 GetDismissButtonLabel() const override;
   bool ShouldShowExtensionList() const override;
-  bool ShouldHighlightExtensions() const override;
   void LogExtensionCount(size_t count) override;
   void LogAction(
       ExtensionMessageBubbleController::BubbleAction action) override;
@@ -128,10 +127,6 @@
   return false;
 }
 
-bool DevModeBubbleDelegate::ShouldHighlightExtensions() const {
-  return true;
-}
-
 void DevModeBubbleDelegate::LogExtensionCount(size_t count) {
   UMA_HISTOGRAM_COUNTS_100(
       "ExtensionBubble.ExtensionsInDevModeCount", count);
diff --git a/chrome/browser/extensions/events_apitest.cc b/chrome/browser/extensions/events_apitest.cc
index 3420022..e70270a2 100644
--- a/chrome/browser/extensions/events_apitest.cc
+++ b/chrome/browser/extensions/events_apitest.cc
@@ -4,6 +4,50 @@
 
 #include "chrome/browser/extensions/extension_apitest.h"
 
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_registry.h"
+
+namespace extensions {
+
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Events) {
   ASSERT_TRUE(RunExtensionTest("events")) << message_;
 }
+
+// Fails on Win only. http://crbug.com/476863
+#if defined(OS_WIN)
+#define MAYBE_EventsAreUnregistered DISABLED_EventsAreUnregistered
+#else
+#define MAYBE_EventsAreUnregistered EventsAreUnregistered
+#endif
+// Tests that events are unregistered when an extension page shuts down.
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_EventsAreUnregistered) {
+  // In this test, page1.html registers for a number of events, then navigates
+  // to page2.html, which should unregister those events. page2.html notifies
+  // pass, by which point the event should have been unregistered.
+  EventRouter* event_router = EventRouter::Get(profile());
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
+
+  std::string test_extension_name = "events_are_unregistered";
+  ASSERT_TRUE(RunExtensionSubtest(test_extension_name, "page1.html"))
+      << message_;
+
+  // Find the extension we just installed by looking for the path.
+  const Extension* extension =
+      GetExtensionByPath(registry->enabled_extensions(),
+                         test_data_dir_.AppendASCII(test_extension_name));
+  ASSERT_TRUE(extension);
+  std::string id = extension->id();
+
+  // The page has closed, so no matter what all events are no longer listened
+  // to.
+  EXPECT_FALSE(
+      event_router->ExtensionHasEventListener(id, "browserAction.onClicked"));
+  EXPECT_FALSE(
+      event_router->ExtensionHasEventListener(id, "runtime.onStartup"));
+  EXPECT_FALSE(
+      event_router->ExtensionHasEventListener(id, "runtime.onSuspend"));
+  EXPECT_FALSE(
+      event_router->ExtensionHasEventListener(id, "runtime.onInstalled"));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_message_bubble.h b/chrome/browser/extensions/extension_message_bubble.h
index 3819a82..71d6e30 100644
--- a/chrome/browser/extensions/extension_message_bubble.h
+++ b/chrome/browser/extensions/extension_message_bubble.h
@@ -15,6 +15,16 @@
 // controller.
 class ExtensionMessageBubble {
  public:
+  // Setup the callback for when the action button is clicked in the
+  // bubble.
+  virtual void OnActionButtonClicked(const base::Closure& callback) = 0;
+
+  // Setup the callback for when the dismiss button is clicked.
+  virtual void OnDismissButtonClicked(const base::Closure& callback) = 0;
+
+  // Setup the callback for when the link is clicked in the bubble.
+  virtual void OnLinkClicked(const base::Closure& callback) = 0;
+
   // Instruct the bubble to appear.
   virtual void Show() = 0;
 };
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.cc b/chrome/browser/extensions/extension_message_bubble_controller.cc
index a30b1f07..778c7ad 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller.cc
@@ -8,7 +8,6 @@
 #include "base/metrics/histogram.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_message_bubble.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -123,15 +122,21 @@
 
 bool ExtensionMessageBubbleController::CloseOnDeactivate() { return false; }
 
-void ExtensionMessageBubbleController::HighlightExtensionsIfNecessary() {
-  if (delegate_->ShouldHighlightExtensions()) {
-    const ExtensionIdList& extension_ids = GetExtensionIdList();
-    DCHECK(!extension_ids.empty());
-    ExtensionToolbarModel::Get(profile_)->HighlightExtensions(extension_ids);
-  }
-}
-
 void ExtensionMessageBubbleController::Show(ExtensionMessageBubble* bubble) {
+  // Wire up all the callbacks, to get notified what actions the user took.
+  base::Closure dismiss_button_callback =
+      base::Bind(&ExtensionMessageBubbleController::OnBubbleDismiss,
+      base::Unretained(this));
+  base::Closure action_button_callback =
+      base::Bind(&ExtensionMessageBubbleController::OnBubbleAction,
+      base::Unretained(this));
+  base::Closure link_callback =
+      base::Bind(&ExtensionMessageBubbleController::OnLinkClicked,
+      base::Unretained(this));
+  bubble->OnActionButtonClicked(action_button_callback);
+  bubble->OnDismissButtonClicked(dismiss_button_callback);
+  bubble->OnLinkClicked(link_callback);
+
   bubble->Show();
 }
 
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.h b/chrome/browser/extensions/extension_message_bubble_controller.h
index e56ff0c..d932e1d 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.h
+++ b/chrome/browser/extensions/extension_message_bubble_controller.h
@@ -59,10 +59,6 @@
     // Whether to show a list of extensions in the bubble.
     virtual bool ShouldShowExtensionList() const = 0;
 
-    // Returns true if the set of affected extensions should be highlighted in
-    // the toolbar.
-    virtual bool ShouldHighlightExtensions() const = 0;
-
     // In some cases, we want the delegate only to handle a single extension
     // and this sets which extension.
     virtual void RestrictToSingleExtension(const std::string& extension_id);
@@ -105,9 +101,6 @@
   // Whether to close the bubble when it loses focus.
   virtual bool CloseOnDeactivate();
 
-  // Highlights the affected extensions if appropriate.
-  void HighlightExtensionsIfNecessary();
-
   // Sets up the callbacks and shows the bubble.
   virtual void Show(ExtensionMessageBubble* bubble);
 
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index 1a50092..40f7c480 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -33,12 +33,6 @@
 #include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
-#include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/chromeos/settings/device_settings_service.h"
-#endif
-
 namespace {
 
 const char kId1[] = "iccfkkhkfiphcjdakkmcjmkfboccmndk";
@@ -213,29 +207,39 @@
     BUBBLE_ACTION_CLICK_LINK,
   };
 
-  FakeExtensionMessageBubble() : controller_(nullptr) {}
+  FakeExtensionMessageBubble() {}
 
   void set_action_on_show(ExtensionBubbleAction action) {
     action_ = action;
   }
-  void set_controller(ExtensionMessageBubbleController* controller) {
-    controller_ = controller;
-  }
 
   void Show() override {
     if (action_ == BUBBLE_ACTION_CLICK_ACTION_BUTTON)
-      controller_->OnBubbleAction();
+      action_callback_.Run();
     else if (action_ == BUBBLE_ACTION_CLICK_DISMISS_BUTTON)
-      controller_->OnBubbleDismiss();
+      dismiss_callback_.Run();
     else if (action_ == BUBBLE_ACTION_CLICK_LINK)
-      controller_->OnLinkClicked();
+      link_callback_.Run();
+  }
+
+  void OnActionButtonClicked(const base::Closure& callback) override {
+    action_callback_ = callback;
+  }
+
+  void OnDismissButtonClicked(const base::Closure& callback) override {
+    dismiss_callback_ = callback;
+  }
+
+  void OnLinkClicked(const base::Closure& callback) override {
+    link_callback_ = callback;
   }
 
  private:
   ExtensionBubbleAction action_;
-  ExtensionMessageBubbleController* controller_;
 
-  DISALLOW_COPY_AND_ASSIGN(FakeExtensionMessageBubble);
+  base::Closure action_callback_;
+  base::Closure dismiss_callback_;
+  base::Closure link_callback_;
 };
 
 class ExtensionMessageBubbleTest : public testing::Test {
@@ -386,6 +390,7 @@
   void Init() {
     // The two lines of magical incantation required to get the extension
     // service to work inside a unit test and access the extension prefs.
+    thread_bundle_.reset(new content::TestBrowserThreadBundle);
     profile_.reset(new TestingProfile);
     static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))
         ->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
@@ -418,22 +423,21 @@
   ExtensionService* service_;
 
  private:
-  content::TestBrowserThreadBundle thread_bundle_;
   scoped_ptr<base::CommandLine> command_line_;
+  scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
   scoped_ptr<TestingProfile> profile_;
 
-#if defined OS_CHROMEOS
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  chromeos::ScopedTestUserManager test_user_manager_;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleTest);
 };
 
-// The feature this is meant to test is only enacted on Windows, but it should
-// pass on all platforms.
-TEST_F(ExtensionMessageBubbleTest, WipeoutControllerTest) {
+// The feature this is meant to test is only implemented on Windows.
+#if defined(OS_WIN)
+#define MAYBE_WipeoutControllerTest WipeoutControllerTest
+#else
+#define MAYBE_WipeoutControllerTest DISABLED_WipeoutControllerTest
+#endif
+
+TEST_F(ExtensionMessageBubbleTest, MAYBE_WipeoutControllerTest) {
   Init();
   // Add three extensions, and control two of them in this test (extension 1
   // and 2).
@@ -470,7 +474,6 @@
   suspicious_extensions = controller->GetExtensionList();
   ASSERT_EQ(1U, suspicious_extensions.size());
   EXPECT_TRUE(base::ASCIIToUTF16("Extension 1") == suspicious_extensions[0]);
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(1U, controller->dismiss_click_count());
@@ -494,16 +497,20 @@
   ASSERT_EQ(2U, suspicious_extensions.size());
   EXPECT_TRUE(base::ASCIIToUTF16("Extension 1") == suspicious_extensions[1]);
   EXPECT_TRUE(base::ASCIIToUTF16("Extension 2") == suspicious_extensions[0]);
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(1U, controller->link_click_count());
   EXPECT_EQ(0U, controller->dismiss_click_count());
   EXPECT_TRUE(controller->delegate()->HasBubbleInfoBeenAcknowledged(kId1));
 }
 
-// The feature this is meant to test is only enacted on Windows, but it should
-// pass on all platforms.
-TEST_F(ExtensionMessageBubbleTest, DevModeControllerTest) {
+// The feature this is meant to test is only implemented on Windows.
+#if defined(OS_WIN)
+#define MAYBE_DevModeControllerTest DevModeControllerTest
+#else
+#define MAYBE_DevModeControllerTest DISABLED_DevModeControllerTest
+#endif
+
+TEST_F(ExtensionMessageBubbleTest, MAYBE_DevModeControllerTest) {
   FeatureSwitch::ScopedOverride force_dev_mode_highlighting(
       FeatureSwitch::force_dev_mode_highlighting(), true);
   Init();
@@ -532,7 +539,6 @@
   FakeExtensionMessageBubble bubble;
   bubble.set_action_on_show(
       FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -550,7 +556,6 @@
   EXPECT_TRUE(controller->ShouldShow());
   dev_mode_extensions = controller->GetExtensionList();
   EXPECT_EQ(2U, dev_mode_extensions.size());
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(1U, controller->action_click_count());
@@ -571,7 +576,6 @@
   EXPECT_TRUE(controller->ShouldShow());
   dev_mode_extensions = controller->GetExtensionList();
   EXPECT_EQ(2U, dev_mode_extensions.size());
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(1U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -639,7 +643,7 @@
             profile(), static_cast<SettingsApiOverrideType>(i)));
 
     // The list will contain one enabled unpacked extension (ext 2).
-    EXPECT_TRUE(controller->ShouldShow());
+    EXPECT_TRUE(controller->ShouldShow(kId2));
     std::vector<base::string16> override_extensions =
         controller->GetExtensionList();
     ASSERT_EQ(1U, override_extensions.size());
@@ -653,7 +657,6 @@
     FakeExtensionMessageBubble bubble;
     bubble.set_action_on_show(
         FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
-    bubble.set_controller(controller.get());
     controller->Show(&bubble);
     EXPECT_EQ(0U, controller->link_click_count());
     EXPECT_EQ(0U, controller->action_click_count());
@@ -675,7 +678,6 @@
         FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_LINK);
     controller.reset(new TestSettingsApiBubbleController(
         profile(), static_cast<SettingsApiOverrideType>(i)));
-    bubble.set_controller(controller.get());
     controller->Show(&bubble);
     EXPECT_EQ(1U, controller->link_click_count());
     EXPECT_EQ(0U, controller->action_click_count());
@@ -696,10 +698,9 @@
         FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_ACTION_BUTTON);
     controller.reset(new TestSettingsApiBubbleController(
         profile(), static_cast<SettingsApiOverrideType>(i)));
-    EXPECT_TRUE(controller->ShouldShow());
+    EXPECT_TRUE(controller->ShouldShow(kId2));
     override_extensions = controller->GetExtensionList();
     EXPECT_EQ(1U, override_extensions.size());
-    bubble.set_controller(controller.get());
     controller->Show(&bubble);  // Simulate showing the bubble.
     EXPECT_EQ(0U, controller->link_click_count());
     EXPECT_EQ(1U, controller->action_click_count());
@@ -729,9 +730,14 @@
   }
 }
 
-// The feature this is meant to test is only enacted on Windows, but it should
-// pass on all platforms.
-TEST_F(ExtensionMessageBubbleTest, NtpOverriddenControllerTest) {
+// The feature this is meant to test is only implemented on Windows.
+#if defined(OS_WIN)
+#define MAYBE_NtpOverriddenControllerTest NtpOverriddenControllerTest
+#else
+#define MAYBE_NtpOverriddenControllerTest DISABLED_NtpOverriddenControllerTest
+#endif
+
+TEST_F(ExtensionMessageBubbleTest, MAYBE_NtpOverriddenControllerTest) {
   Init();
   // Load two extensions overriding new tab page and one overriding something
   // unrelated (to check for interference). Extension 2 should still win
@@ -759,7 +765,6 @@
   bubble.set_action_on_show(
       FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
   EXPECT_TRUE(controller->ShouldShow(kId2));
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -781,7 +786,6 @@
       FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_LINK);
   controller.reset(new TestNtpOverriddenBubbleController(profile()));
   EXPECT_TRUE(controller->ShouldShow(kId2));
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);
   EXPECT_EQ(1U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -804,7 +808,6 @@
   EXPECT_TRUE(controller->ShouldShow(kId2));
   override_extensions = controller->GetExtensionList();
   EXPECT_EQ(1U, override_extensions.size());
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(1U, controller->action_click_count());
@@ -892,7 +895,6 @@
   FakeExtensionMessageBubble bubble;
   bubble.set_action_on_show(
       FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -914,7 +916,6 @@
       FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_LINK);
   controller.reset(new TestProxyOverriddenBubbleController(profile()));
   EXPECT_TRUE(controller->ShouldShow(kId2));
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);
   EXPECT_EQ(1U, controller->link_click_count());
   EXPECT_EQ(0U, controller->action_click_count());
@@ -937,7 +938,6 @@
   EXPECT_TRUE(controller->ShouldShow(kId2));
   override_extensions = controller->GetExtensionList();
   EXPECT_EQ(1U, override_extensions.size());
-  bubble.set_controller(controller.get());
   controller->Show(&bubble);  // Simulate showing the bubble.
   EXPECT_EQ(0U, controller->link_click_count());
   EXPECT_EQ(1U, controller->action_click_count());
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index d61a48d2..90acea6 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -198,20 +198,17 @@
 // extensions installed and see them run and do basic things.
 typedef ExtensionStartupTestBase ExtensionsStartupTest;
 
-IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, Test) {
+// Broken in official builds, http://crbug.com/474659
+IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, DISABLED_Test) {
   WaitForServicesToStart(num_expected_extensions_, true);
   TestInjection(true, true);
 }
 
+// Broken in official builds, http://crbug.com/474659
 // Sometimes times out on Mac.  http://crbug.com/48151
-#if defined(OS_MACOSX)
-#define MAYBE_NoFileAccess DISABLED_NoFileAccess
-#else
-#define MAYBE_NoFileAccess NoFileAccess
-#endif
 // Tests that disallowing file access on an extension prevents it from injecting
 // script into a page with a file URL.
-IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, MAYBE_NoFileAccess) {
+IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, DISABLED_NoFileAccess) {
   WaitForServicesToStart(num_expected_extensions_, true);
 
   // Keep a separate list of extensions for which to disable file access, since
diff --git a/chrome/browser/extensions/external_component_loader.cc b/chrome/browser/extensions/external_component_loader.cc
index d536187..4df086a6 100644
--- a/chrome/browser/extensions/external_component_loader.cc
+++ b/chrome/browser/extensions/external_component_loader.cc
@@ -4,12 +4,14 @@
 
 #include "chrome/browser/extensions/external_component_loader.h"
 
+#include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/bookmarks/enhanced_bookmarks_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/search/hotword_service.h"
 #include "chrome/browser/search/hotword_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "extensions/common/extension.h"
@@ -79,6 +81,16 @@
   }
 #endif
 
+
+#if defined(ENABLE_MEDIA_ROUTER)
+  if (switches::MediaRouterEnabled()) {
+    std::string media_router_extension_id(
+        extension_misc::kMediaRouterStableExtensionId);
+    prefs_->SetString(media_router_extension_id + ".external_update_url",
+                      extension_urls::GetWebstoreUpdateUrl().spec());
+  }
+#endif  // defined(ENABLE_MEDIA_ROUTER)
+
 #if defined(ENABLE_APP_LIST) && defined(OS_CHROMEOS)
   std::string google_now_extension_id;
   if (GetGoogleNowExtensionId(&google_now_extension_id)) {
diff --git a/chrome/browser/extensions/ntp_overridden_bubble_controller.cc b/chrome/browser/extensions/ntp_overridden_bubble_controller.cc
index 317b7bae..bec4ed3c 100644
--- a/chrome/browser/extensions/ntp_overridden_bubble_controller.cc
+++ b/chrome/browser/extensions/ntp_overridden_bubble_controller.cc
@@ -47,7 +47,6 @@
   base::string16 GetActionButtonLabel() const override;
   base::string16 GetDismissButtonLabel() const override;
   bool ShouldShowExtensionList() const override;
-  bool ShouldHighlightExtensions() const override;
   void RestrictToSingleExtension(const std::string& extension_id) override;
   void LogExtensionCount(size_t count) override;
   void LogAction(extensions::ExtensionMessageBubbleController::BubbleAction
@@ -143,10 +142,6 @@
   return false;
 }
 
-bool NtpOverriddenBubbleDelegate::ShouldHighlightExtensions() const {
-  return false;
-}
-
 void NtpOverriddenBubbleDelegate::RestrictToSingleExtension(
     const std::string& extension_id) {
   extension_id_ = extension_id;
diff --git a/chrome/browser/extensions/proxy_overridden_bubble_controller.cc b/chrome/browser/extensions/proxy_overridden_bubble_controller.cc
index d25204d..a3cde4c 100644
--- a/chrome/browser/extensions/proxy_overridden_bubble_controller.cc
+++ b/chrome/browser/extensions/proxy_overridden_bubble_controller.cc
@@ -51,7 +51,6 @@
   base::string16 GetActionButtonLabel() const override;
   base::string16 GetDismissButtonLabel() const override;
   bool ShouldShowExtensionList() const override;
-  bool ShouldHighlightExtensions() const override;
   void RestrictToSingleExtension(const std::string& extension_id) override;
   void LogExtensionCount(size_t count) override;
   void LogAction(
@@ -162,10 +161,6 @@
   return false;
 }
 
-bool ProxyOverriddenBubbleDelegate::ShouldHighlightExtensions() const {
-  return true;
-}
-
 void ProxyOverriddenBubbleDelegate::RestrictToSingleExtension(
     const std::string& extension_id) {
   extension_id_ = extension_id;
diff --git a/chrome/browser/extensions/settings_api_bubble_controller.cc b/chrome/browser/extensions/settings_api_bubble_controller.cc
index e90cf93..e89f42f 100644
--- a/chrome/browser/extensions/settings_api_bubble_controller.cc
+++ b/chrome/browser/extensions/settings_api_bubble_controller.cc
@@ -54,7 +54,6 @@
   base::string16 GetActionButtonLabel() const override;
   base::string16 GetDismissButtonLabel() const override;
   bool ShouldShowExtensionList() const override;
-  bool ShouldHighlightExtensions() const override;
   void LogExtensionCount(size_t count) override;
   void LogAction(
       ExtensionMessageBubbleController::BubbleAction action) override;
@@ -245,10 +244,6 @@
   return false;
 }
 
-bool SettingsApiBubbleDelegate::ShouldHighlightExtensions() const {
-  return type_ == BUBBLE_TYPE_STARTUP_PAGES;
-}
-
 void SettingsApiBubbleDelegate::LogExtensionCount(size_t count) {
 }
 
@@ -295,27 +290,11 @@
 
 SettingsApiBubbleController::~SettingsApiBubbleController() {}
 
-bool SettingsApiBubbleController::ShouldShow() {
-  const Extension* extension = nullptr;
-  switch (type_) {
-    case BUBBLE_TYPE_HOME_PAGE:
-      extension = GetExtensionOverridingHomepage(profile_);
-      break;
-    case BUBBLE_TYPE_SEARCH_ENGINE:
-      extension = GetExtensionOverridingSearchEngine(profile_);
-      break;
-    case BUBBLE_TYPE_STARTUP_PAGES:
-      extension = GetExtensionOverridingStartupPages(profile_);
-      break;
-  }
-
-  if (!extension)
+bool SettingsApiBubbleController::ShouldShow(const std::string& extension_id) {
+  if (delegate()->HasBubbleInfoBeenAcknowledged(extension_id))
     return false;
 
-  if (delegate()->HasBubbleInfoBeenAcknowledged(extension->id()))
-    return false;
-
-  if (!delegate()->ShouldIncludeExtension(extension->id()))
+  if (!delegate()->ShouldIncludeExtension(extension_id))
     return false;
 
   // If the browser is showing the 'Chrome crashed' infobar, it won't be showing
diff --git a/chrome/browser/extensions/settings_api_bubble_controller.h b/chrome/browser/extensions/settings_api_bubble_controller.h
index 42095330..df64077 100644
--- a/chrome/browser/extensions/settings_api_bubble_controller.h
+++ b/chrome/browser/extensions/settings_api_bubble_controller.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_EXTENSIONS_SETTINGS_API_BUBBLE_CONTROLLER_H_
 
 #include <string>
-
 #include "chrome/browser/extensions/extension_message_bubble_controller.h"
 #include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
 
@@ -19,9 +18,9 @@
   SettingsApiBubbleController(Profile* profile, SettingsApiOverrideType type);
   ~SettingsApiBubbleController() override;
 
-  // Returns true if we should show the bubble for the extension actively
-  // overriding the setting of |type_|.
-  bool ShouldShow();
+  // Whether the controller knows that we should show the bubble for extension
+  // with |extension_id|. Returns true if so.
+  bool ShouldShow(const std::string& extension_id);
 
   // ExtensionMessageBubbleController:
   bool CloseOnDeactivate() override;
diff --git a/chrome/browser/extensions/suspicious_extension_bubble_controller.cc b/chrome/browser/extensions/suspicious_extension_bubble_controller.cc
index 94d189f..da364b2 100644
--- a/chrome/browser/extensions/suspicious_extension_bubble_controller.cc
+++ b/chrome/browser/extensions/suspicious_extension_bubble_controller.cc
@@ -53,7 +53,6 @@
   base::string16 GetActionButtonLabel() const override;
   base::string16 GetDismissButtonLabel() const override;
   bool ShouldShowExtensionList() const override;
-  bool ShouldHighlightExtensions() const override;
   void LogExtensionCount(size_t count) override;
   void LogAction(
       ExtensionMessageBubbleController::BubbleAction action) override;
@@ -127,14 +126,11 @@
   return l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNSUPPORTED_DISABLED_BUTTON);
 }
 
-bool SuspiciousExtensionBubbleDelegate::ShouldShowExtensionList() const {
+bool
+SuspiciousExtensionBubbleDelegate::ShouldShowExtensionList() const {
   return true;
 }
 
-bool SuspiciousExtensionBubbleDelegate::ShouldHighlightExtensions() const {
-  return false;
-}
-
 void SuspiciousExtensionBubbleDelegate::LogExtensionCount(
     size_t count) {
   UMA_HISTOGRAM_COUNTS_100(
diff --git a/chrome/browser/favicon/DEPS b/chrome/browser/favicon/DEPS
index 7bc0c6f..bd86119 100644
--- a/chrome/browser/favicon/DEPS
+++ b/chrome/browser/favicon/DEPS
@@ -1,39 +1,14 @@
 include_rules = [
-  # Favicon is being made into a Browser Component, so we have these
-  # three basic rules followed by temporary exceptions.  Please don't
-  # add to the list of exceptions!
-  "-chrome/browser",
-  "+chrome/browser/chrome_notification_types.h",
-  "+chrome/browser/common",
-  "+chrome/browser/favicon",
-
-  # Permanently-allowed DEPS beyond the standard Browser
-  # Components-like DEPS above go here.
-  "+chrome/browser/profiles/incognito_helpers.h",
-  "+chrome/browser/search/search.h",
-  "+components/favicon/core",
-  "+net/base/registry_controlled_domains/registry_controlled_domain.h",
-  "+third_party/skia/include/core/SkBitmap.h",
-
-  # TODO(caitkp): Bring this list to zero.
+  # Favicon is a layered component [1]. You will only find Chrome specific
+  # factories and helper in that directory, the meat of the components is
+  # located at //components/favicon.
   #
-  # Do not add to the list of temporarily-allowed dependencies below,
-  # and please do not introduce more #includes of these files.
-  "!chrome/browser/bookmarks/bookmark_model_factory.h",
-  "!chrome/browser/history/history_backend.h",
-  "!chrome/browser/history/history_service.h",
-  "!chrome/browser/history/history_service_factory.h",
-  "!chrome/browser/history/select_favicon_frames.h",
-  "!chrome/browser/profiles/profile.h",
-  "!chrome/browser/ui/webui/chrome_web_ui_controller_factory.h",
-  "!components/bookmarks/browser/bookmark_model.h",
-  # Do not add to the list of temporarily-allowed dependencies above,
-  # and please do not introduce more #includes of these files.
+  # [1]: http://www.chromium.org/developers/design-documents/layered-components-design
+  "+chrome/browser",
 ]
 
 specific_include_rules = {
-  # Browser tests, by definition, need access to the browser objects.
-  '.*_browsertest\.cc': [
-    "+chrome/browser",
+  '.*test\.cc': [
+    "+chrome/test",
   ]
 }
diff --git a/chrome/browser/favicon/OWNERS b/chrome/browser/favicon/OWNERS
index f061d752..c888e8735 100644
--- a/chrome/browser/favicon/OWNERS
+++ b/chrome/browser/favicon/OWNERS
@@ -3,7 +3,4 @@
 stevenjb@chromium.org
 
 # Temporary owner, for refactoring changes only.
-sdefresne@chromium.org
-
-# Temporary owner, for refactoring changes only.
 caitkp@chromium.org
diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc
index dc6dbdf..32a26d2 100644
--- a/chrome/browser/history/expire_history_backend_unittest.cc
+++ b/chrome/browser/history/expire_history_backend_unittest.cc
@@ -463,8 +463,9 @@
   EXPECT_TRUE(HasFavicon(favicon_id));
 }
 
-// DeleteURL should not delete starred urls.
-TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
+// DeleteURL should delete the history of starred urls, but the URL should
+// remain starred and its favicon should remain too.
+TEST_F(ExpireHistoryTest, DeleteStarredVisitedURL) {
   URLID url_ids[3];
   Time visit_times[4];
   AddExampleData(url_ids, visit_times);
@@ -478,31 +479,44 @@
   // Attempt to delete the url.
   expirer_.DeleteURL(url_row.url());
 
-  // Because the url is starred, it shouldn't be deleted.
+  // Verify it no longer exists.
   GURL url = url_row.url();
-  ASSERT_TRUE(main_db_->GetRowForURL(url, &url_row));
+  ASSERT_FALSE(main_db_->GetRowForURL(url, &url_row));
+  EnsureURLInfoGone(url_row, false);
 
-  // And the favicon should exist.
+  // Yet the favicon should exist.
   favicon_base::FaviconID favicon_id =
-      GetFavicon(url_row.url(), favicon_base::FAVICON);
+      GetFavicon(url, favicon_base::FAVICON);
   EXPECT_TRUE(HasFavicon(favicon_id));
 
-  // And no visits.
-  VisitVector visits;
-  main_db_->GetVisitsForURL(url_row.id(), &visits);
-  ASSERT_EQ(0U, visits.size());
-
   // Should still have the thumbnail.
   // TODO(sky): fix this, see comment in HasThumbnail.
   // ASSERT_TRUE(HasThumbnail(url_row.id()));
+}
 
-  // Unstar the URL and delete again.
-  history_client_.ClearAllBookmarks();
-  ClearLastNotifications();
+// DeleteURL should not delete the favicon of bookmarked URLs.
+TEST_F(ExpireHistoryTest, DeleteStarredUnvisitedURL) {
+  // Create a bookmark associated with a favicon.
+  const GURL url("http://www.google.com/starred");
+  favicon_base::FaviconID favicon =
+      thumb_db_->AddFavicon(GURL("http://favicon/url1"), favicon_base::FAVICON);
+  thumb_db_->AddIconMapping(url, favicon);
+  StarURL(url);
+
+  // Delete it.
   expirer_.DeleteURL(url);
 
-  // Now it should be completely deleted.
-  EnsureURLInfoGone(url_row, false);
+  // The favicon should exist.
+  favicon_base::FaviconID favicon_id = GetFavicon(url, favicon_base::FAVICON);
+  EXPECT_TRUE(HasFavicon(favicon_id));
+
+  // Unstar the URL and try again to delete it.
+  history_client_.ClearAllBookmarks();
+  expirer_.DeleteURL(url);
+
+  // The favicon should be gone.
+  favicon_id = GetFavicon(url, favicon_base::FAVICON);
+  EXPECT_FALSE(HasFavicon(favicon_id));
 }
 
 // Deletes multiple URLs at once.  The favicon for the third one but
@@ -533,8 +547,7 @@
   // Delete the URLs and their dependencies.
   expirer_.DeleteURLs(urls);
 
-  // First one should still be around (since it was starred).
-  ASSERT_TRUE(main_db_->GetRowForURL(rows[0].url(), &rows[0]));
+  EnsureURLInfoGone(rows[0], false);
   EnsureURLInfoGone(rows[1], false);
   EnsureURLInfoGone(rows[2], false);
   EXPECT_TRUE(HasFavicon(favicon_ids[0]));
diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc
index c6de4d0..c578922 100644
--- a/chrome/browser/history/history_backend_unittest.cc
+++ b/chrome/browser/history/history_backend_unittest.cc
@@ -796,13 +796,9 @@
   history_client_.AddBookmark(row1.url());
   history_client_.AddBookmark(row2.url());
 
-  // Delete url 2. Because url 2 is starred this won't delete the URL, only
-  // the visits.
+  // Delete url 2.
   backend_->expirer_.DeleteURL(row2.url());
-
-  // Make sure url 2 is still valid, but has no visits.
-  URLRow tmp_url_row;
-  EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
+  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
   VisitVector visits;
   backend_->db_->GetVisitsForURL(row2_id, &visits);
   EXPECT_EQ(0U, visits.size());
@@ -820,8 +816,8 @@
   unstarred_urls.insert(row2.url());
   backend_->URLsNoLongerBookmarked(unstarred_urls);
 
-  // The URL should no longer exist.
-  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
+  // The URL should still not exist.
+  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), NULL));
   // And the favicon should be deleted.
   EXPECT_EQ(0,
             backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
diff --git a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
index 2faefc5b..db3c0812 100644
--- a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
+++ b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
@@ -582,6 +582,35 @@
                               validate_media_files())));
 }
 
+void DeviceMediaAsyncFileUtil::AddWatcher(
+    const storage::FileSystemURL& url,
+    bool recursive,
+    const storage::WatcherManager::StatusCallback& callback,
+    const storage::WatcherManager::NotificationCallback&
+        notification_callback) {
+  MTPDeviceAsyncDelegate* const delegate = GetMTPDeviceDelegate(url);
+  if (!delegate) {
+    callback.Run(base::File::FILE_ERROR_FAILED);
+    return;
+  }
+
+  delegate->AddWatcher(url.origin(), url.path(), recursive, callback,
+                       notification_callback);
+}
+
+void DeviceMediaAsyncFileUtil::RemoveWatcher(
+    const storage::FileSystemURL& url,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback) {
+  MTPDeviceAsyncDelegate* const delegate = GetMTPDeviceDelegate(url);
+  if (!delegate) {
+    callback.Run(base::File::FILE_ERROR_FAILED);
+    return;
+  }
+
+  delegate->RemoveWatcher(url.origin(), url.path(), recursive, callback);
+}
+
 DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil(
     const base::FilePath& profile_path,
     MediaFileValidationType validation_type)
diff --git a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.h b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.h
index 5683a73..6b32d849 100644
--- a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.h
+++ b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "storage/browser/blob/shareable_file_reference.h"
 #include "storage/browser/fileapi/async_file_util.h"
+#include "storage/browser/fileapi/watcher_manager.h"
 
 namespace storage {
 class FileSystemOperationContext;
@@ -108,6 +109,18 @@
       const base::Time& expected_modification_time,
       storage::FileSystemContext* context);
 
+  // Adds watcher to |url|.
+  void AddWatcher(const storage::FileSystemURL& url,
+                  bool recursive,
+                  const storage::WatcherManager::StatusCallback& callback,
+                  const storage::WatcherManager::NotificationCallback&
+                      notification_callback);
+
+  // Removes watcher of |url|.
+  void RemoveWatcher(const storage::FileSystemURL& url,
+                     const bool recursive,
+                     const storage::WatcherManager::StatusCallback& callback);
+
  private:
   class MediaPathFilterWrapper;
 
diff --git a/chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h b/chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h
index b25eef9..2ebfa939e 100644
--- a/chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h
+++ b/chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h
@@ -9,6 +9,8 @@
 #include "base/files/file.h"
 #include "base/memory/ref_counted.h"
 #include "storage/browser/fileapi/async_file_util.h"
+#include "storage/browser/fileapi/watcher_manager.h"
+#include "url/gurl.h"
 
 namespace base {
 class FilePath;
@@ -176,6 +178,22 @@
       const DeleteDirectorySuccessCallback& success_callback,
       const ErrorCallback& error_callback) = 0;
 
+  // Adds watcher to |file_path| as |origin|.
+  virtual void AddWatcher(
+      const GURL& origin,
+      const base::FilePath& file_path,
+      const bool recursive,
+      const storage::WatcherManager::StatusCallback& callback,
+      const storage::WatcherManager::NotificationCallback&
+          notification_callback) = 0;
+
+  // Removes watcher from |file_path| of |origin|.
+  virtual void RemoveWatcher(
+      const GURL& origin,
+      const base::FilePath& file_path,
+      const bool recursive,
+      const storage::WatcherManager::StatusCallback& callback) = 0;
+
   // Called when the
   // (1) Browser application is in shutdown mode (or)
   // (2) Last extension using this MTP device is destroyed (or)
diff --git a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
index b6f8a346..5eadd21b 100644
--- a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
+++ b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
@@ -754,6 +754,76 @@
                                        FROM_HERE, closure));
 }
 
+void MTPDeviceDelegateImplLinux::AddWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback,
+    const storage::WatcherManager::NotificationCallback&
+        notification_callback) {
+  if (recursive) {
+    callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+    return;
+  }
+
+  // TODO(yawano) Checks existence of |file_path|.
+  const auto it = subscribers_.find(file_path);
+  if (it != subscribers_.end()) {
+    // Adds to existing origin callback map.
+    if (ContainsKey(it->second, origin)) {
+      callback.Run(base::File::FILE_ERROR_EXISTS);
+      return;
+    }
+
+    it->second.insert(std::make_pair(origin, notification_callback));
+  } else {
+    // Creates new origin callback map.
+    OriginNotificationCallbackMap callback_map;
+    callback_map.insert(std::make_pair(origin, notification_callback));
+    subscribers_.insert(std::make_pair(file_path, callback_map));
+  }
+
+  callback.Run(base::File::FILE_OK);
+}
+
+void MTPDeviceDelegateImplLinux::RemoveWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback) {
+  if (recursive) {
+    callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+    return;
+  }
+
+  const auto it = subscribers_.find(file_path);
+  if (it == subscribers_.end()) {
+    callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+    return;
+  }
+
+  if (it->second.erase(origin) == 0) {
+    callback.Run(base::File::FILE_ERROR_NOT_FOUND);
+    return;
+  }
+
+  if (it->second.empty())
+    subscribers_.erase(it);
+
+  callback.Run(base::File::FILE_OK);
+}
+
+void MTPDeviceDelegateImplLinux::NotifyFileChange(
+    const base::FilePath& file_path,
+    const storage::WatcherManager::ChangeType change_type) {
+  const auto it = subscribers_.find(file_path);
+  if (it != subscribers_.end()) {
+    for (const auto& origin_callback : it->second) {
+      origin_callback.second.Run(change_type);
+    }
+  }
+}
+
 void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object.
@@ -1343,9 +1413,9 @@
   }
 
   const MTPDeviceTaskHelper::CreateDirectorySuccessCallback
-      success_callback_wrapper =
-          base::Bind(&MTPDeviceDelegateImplLinux::OnDidCreateSingleDirectory,
-                     weak_ptr_factory_.GetWeakPtr(), success_callback);
+      success_callback_wrapper = base::Bind(
+          &MTPDeviceDelegateImplLinux::OnDidCreateSingleDirectory,
+          weak_ptr_factory_.GetWeakPtr(), directory_path, success_callback);
   const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper =
       base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
                  weak_ptr_factory_.GetWeakPtr(), error_callback, parent_id);
@@ -1448,10 +1518,13 @@
 }
 
 void MTPDeviceDelegateImplLinux::OnDidCreateSingleDirectory(
+    const base::FilePath& directory_path,
     const CreateDirectorySuccessCallback& success_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
   success_callback.Run();
+  NotifyFileChange(directory_path.DirName(),
+                   storage::WatcherManager::ChangeType::CHANGED);
   PendingRequestDone();
 }
 
diff --git a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
index b11eef3..3242740 100644
--- a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
+++ b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h
@@ -124,6 +124,17 @@
   void DeleteDirectory(const base::FilePath& file_path,
                        const DeleteDirectorySuccessCallback& success_callback,
                        const ErrorCallback& error_callback) override;
+  void AddWatcher(const GURL& origin,
+                  const base::FilePath& file_path,
+                  const bool recursive,
+                  const storage::WatcherManager::StatusCallback& callback,
+                  const storage::WatcherManager::NotificationCallback&
+                      notification_callback) override;
+  void RemoveWatcher(
+      const GURL& origin,
+      const base::FilePath& file_path,
+      const bool recursive,
+      const storage::WatcherManager::StatusCallback& callback) override;
   void CancelPendingTasksAndDeleteDelegate() override;
 
   // The internal methods correspond to the similarly named methods above.
@@ -206,6 +217,10 @@
       const DeleteObjectSuccessCallback& success_callback,
       const ErrorCallback& error_callback);
 
+  // Notifies |chage_type| of |file_path| to watchers.
+  void NotifyFileChange(const base::FilePath& file_path,
+                        const storage::WatcherManager::ChangeType change_type);
+
   // Ensures the device is initialized for communication.
   // If the device is already initialized, call RunTask().
   //
@@ -303,6 +318,7 @@
 
   // Called when CreateSignleDirectory() succeeds.
   void OnDidCreateSingleDirectory(
+      const base::FilePath& directory_path,
       const CreateDirectorySuccessCallback& success_callback);
 
   // Called when parent directory |created_directory| is created as part of
@@ -458,6 +474,12 @@
   // Mode for opening storage.
   const bool read_only_;
 
+  // Maps for holding notification callbacks.
+  typedef std::map<GURL, storage::WatcherManager::NotificationCallback>
+      OriginNotificationCallbackMap;
+  typedef std::map<base::FilePath, OriginNotificationCallbackMap> Subscribers;
+  Subscribers subscribers_;
+
   // A list of pending tasks that needs to be run when the device is
   // initialized or when the current task in progress is complete.
   std::deque<PendingTaskInfo> pending_tasks_;
diff --git a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h
index d01874c7..c92da81 100644
--- a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h
+++ b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h
@@ -85,6 +85,17 @@
   void DeleteDirectory(const base::FilePath& file_path,
                        const DeleteDirectorySuccessCallback& success_callback,
                        const ErrorCallback& error_callback) override;
+  void AddWatcher(const GURL& origin,
+                  const base::FilePath& file_path,
+                  const bool recursive,
+                  const storage::WatcherManager::StatusCallback& callback,
+                  const storage::WatcherManager::NotificationCallback&
+                      notification_callback) override;
+  void RemoveWatcher(
+      const GURL& origin,
+      const base::FilePath& file_path,
+      const bool recursive,
+      const storage::WatcherManager::StatusCallback& callback) override;
   void CancelPendingTasksAndDeleteDelegate() override;
 
   // Forward delegates for ImageCaptureDeviceListener. These are
diff --git a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.mm b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.mm
index 56b39ae..89e5108 100644
--- a/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.mm
+++ b/chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.mm
@@ -263,6 +263,26 @@
   NOTREACHED();
 }
 
+void MTPDeviceDelegateImplMac::AddWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback,
+    const storage::WatcherManager::NotificationCallback&
+        notification_callback) {
+  NOTIMPLEMENTED();
+  callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+}
+
+void MTPDeviceDelegateImplMac::RemoveWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+}
+
 void MTPDeviceDelegateImplMac::CancelPendingTasksAndDeleteDelegate() {
   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
       base::Bind(&MTPDeviceDelegateImplMac::CancelAndDelete,
diff --git a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
index 8028cc5..fe563d6 100644
--- a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
+++ b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.cc
@@ -515,6 +515,26 @@
   NOTREACHED();
 }
 
+void MTPDeviceDelegateImplWin::AddWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback,
+    const storage::WatcherManager::NotificationCallback&
+        notification_callback) {
+  NOTIMPLEMENTED();
+  callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+}
+
+void MTPDeviceDelegateImplWin::RemoveWatcher(
+    const GURL& origin,
+    const base::FilePath& file_path,
+    const bool recursive,
+    const storage::WatcherManager::StatusCallback& callback) {
+  NOTIMPLEMENTED();
+  callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
+}
+
 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
diff --git a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h
index c90741d..11f3205 100644
--- a/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h
+++ b/chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h
@@ -140,6 +140,17 @@
   void DeleteDirectory(const base::FilePath& file_path,
                        const DeleteDirectorySuccessCallback& success_callback,
                        const ErrorCallback& error_callback) override;
+  void AddWatcher(const GURL& origin,
+                  const base::FilePath& file_path,
+                  const bool recursive,
+                  const storage::WatcherManager::StatusCallback& callback,
+                  const storage::WatcherManager::NotificationCallback&
+                      notification_callback) override;
+  void RemoveWatcher(
+      const GURL& origin,
+      const base::FilePath& file_path,
+      const bool recursive,
+      const storage::WatcherManager::StatusCallback& callback) override;
   virtual void CancelPendingTasksAndDeleteDelegate() override;
 
   // Ensures the device is initialized for communication by doing a
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
index e6e8ae21..66cbc15 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
@@ -60,7 +60,7 @@
       data_reduction_proxy_ui_service(
           new data_reduction_proxy::ContentDataReductionProxyDebugUIService(
               base::Bind(&data_reduction_proxy::DataReductionProxyConfigurator::
-                             GetProxyConfigOnIOThread,
+                             GetProxyConfig,
                          base::Unretained(
                              data_reduction_proxy_io_data->configurator())),
               ui_task_runner, io_task_runner,
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc
index fba3d006..78b24257 100644
--- a/chrome/browser/password_manager/password_store_factory.cc
+++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -13,6 +13,8 @@
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/glue/sync_start_util.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/webdata/web_data_service_factory.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
@@ -81,6 +83,49 @@
       base::TimeDelta::FromSeconds(40));
 }
 
+base::FilePath GetAffiliationDatabasePath(Profile* profile) {
+  DCHECK(profile);
+  return profile->GetPath().Append(chrome::kAffiliationDatabaseFileName);
+}
+
+bool ShouldAffiliationBasedMatchingBeActive(Profile* profile) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!password_manager::IsAffiliationBasedMatchingEnabled(*command_line))
+    return false;
+
+  DCHECK(profile);
+  ProfileSyncService* profile_sync_service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+  return profile_sync_service &&
+         profile_sync_service->IsSyncEnabledAndLoggedIn() &&
+         profile_sync_service->SyncActive() &&
+         profile_sync_service->GetPreferredDataTypes().Has(syncer::PASSWORDS) &&
+         !profile_sync_service->IsUsingSecondaryPassphrase();
+}
+
+void ActivateAffiliationBasedMatching(PasswordStore* password_store,
+                                      Profile* profile) {
+  DCHECK(password_store);
+  DCHECK(profile);
+
+  // The PasswordStore is so far the only consumer of the AffiliationService,
+  // therefore the service is owned by the AffiliatedMatchHelper, which in
+  // turn is owned by the PasswordStore.
+  // TODO(engedy): Double-check which request context we want.
+  scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner(
+      content::BrowserThread::GetMessageLoopProxyForThread(
+          content::BrowserThread::DB));
+  scoped_ptr<password_manager::AffiliationService> affiliation_service(
+      new password_manager::AffiliationService(db_thread_runner));
+  affiliation_service->Initialize(profile->GetRequestContext(),
+                                  GetAffiliationDatabasePath(profile));
+  scoped_ptr<password_manager::AffiliatedMatchHelper> affiliated_match_helper(
+      new password_manager::AffiliatedMatchHelper(password_store,
+                                                  affiliation_service.Pass()));
+  affiliated_match_helper->Initialize();
+  password_store->SetAffiliatedMatchHelper(affiliated_match_helper.Pass());
+}
+
 }  // namespace
 
 
@@ -121,6 +166,24 @@
   return Singleton<PasswordStoreFactory>::get();
 }
 
+// static
+void PasswordStoreFactory::OnPasswordsSyncedStatePotentiallyChanged(
+    Profile* profile) {
+  scoped_refptr<PasswordStore> password_store =
+      GetForProfile(profile, ServiceAccessType::EXPLICIT_ACCESS);
+  if (!password_store)
+    return;
+
+  if (ShouldAffiliationBasedMatchingBeActive(profile) &&
+      !password_store->HasAffiliatedMatchHelper()) {
+    ActivateAffiliationBasedMatching(password_store.get(), profile);
+  } else if (!ShouldAffiliationBasedMatchingBeActive(profile) &&
+             password_store->HasAffiliatedMatchHelper()) {
+    password_store->SetAffiliatedMatchHelper(
+        make_scoped_ptr<password_manager::AffiliatedMatchHelper>(nullptr));
+  }
+}
+
 PasswordStoreFactory::PasswordStoreFactory()
     : BrowserContextKeyedServiceFactory(
         "PasswordStore",
@@ -277,24 +340,6 @@
     return nullptr;
   }
 
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (password_manager::IsAffiliationBasedMatchingEnabled(*command_line)) {
-    // The PasswordStore is so far the only consumer of the AffiliationService,
-    // therefore the service is owned by the AffiliatedMatchHelper, which in
-    // turn is owned by the PasswordStore.
-    // TODO(engedy): Double-check which request context we want.
-    scoped_ptr<password_manager::AffiliationService> affiliation_service(
-        new password_manager::AffiliationService(db_thread_runner));
-    affiliation_service->Initialize(
-        profile->GetRequestContext(),
-        profile->GetPath().Append(chrome::kAffiliationDatabaseFileName));
-    scoped_ptr<password_manager::AffiliatedMatchHelper> affiliated_match_helper(
-        new password_manager::AffiliatedMatchHelper(
-            ps.get(), affiliation_service.Pass()));
-    affiliated_match_helper->Initialize();
-    ps->SetAffiliatedMatchHelper(affiliated_match_helper.Pass());
-  }
-
   return new PasswordStoreService(ps);
 }
 
diff --git a/chrome/browser/password_manager/password_store_factory.h b/chrome/browser/password_manager/password_store_factory.h
index d11d2b8..25bd747 100644
--- a/chrome/browser/password_manager/password_store_factory.h
+++ b/chrome/browser/password_manager/password_store_factory.h
@@ -58,6 +58,10 @@
 
   static PasswordStoreFactory* GetInstance();
 
+  // Called by the PasswordDataTypeController whenever there is a possibility
+  // that syncing passwords has just started or ended for |profile|.
+  static void OnPasswordsSyncedStatePotentiallyChanged(Profile* profile);
+
  private:
   friend struct DefaultSingletonTraits<PasswordStoreFactory>;
 
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index b502385..46623e7 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -320,7 +320,7 @@
 
 // The EndSession IO synchronization is only critical on Windows, but also
 // happens under the USE_X11 define. See BrowserProcessImpl::EndSession.
-#if defined(USE_X11) || defined(OS_WIN)
+#if defined(USE_X11) || defined(OS_WIN) || defined(USE_OZONE)
 
 namespace {
 
@@ -408,4 +408,4 @@
   ASSERT_TRUE(succeeded) << "profile->EndSession() timed out too often.";
 }
 
-#endif  // defined(USE_X11) || defined(OS_WIN)
+#endif  // defined(USE_X11) || defined(OS_WIN) || defined(USE_OZONE)
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 397d05b..9a1b15ca 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -116,19 +116,6 @@
   std::string app_id_;
 };
 
-// The Push API depends on Web Notifications, which is only available on Android
-// Jelly Bean and later.
-bool IsPushSupported() {
-#if defined(OS_ANDROID)
-  if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_JELLY_BEAN) {
-    DVLOG(0) << "The Push API is only supported in Android 4.1 and later.";
-    return false;
-  }
-#endif
-  return true;
-}
-
 }  // namespace
 
 class PushMessagingBrowserTest : public InProcessBrowserTest {
@@ -250,9 +237,6 @@
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBadManifestBrowserTest,
                        RegisterFailsNotVisibleMessages) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -289,9 +273,6 @@
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        RegisterSuccessNotificationsGranted) {
-  if (!IsPushSupported())
-    return;
-
   TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
 
   PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL);
@@ -301,9 +282,6 @@
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        RegisterSuccessNotificationsPrompt) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -320,9 +298,6 @@
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        RegisterFailureNotificationsBlocked) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -338,9 +313,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, RegisterFailureNoManifest) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -361,9 +333,6 @@
 // TODO(johnme): Test registering from a worker - see https://crbug.com/437298.
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, RegisterPersisted) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   // First, test that Service Worker registration IDs are assigned in order of
@@ -418,9 +387,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
@@ -446,9 +412,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
@@ -491,9 +454,6 @@
 #if defined(ENABLE_NOTIFICATIONS)
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        PushEventEnforcesUserVisibleNotification) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
@@ -590,9 +550,6 @@
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        PushEventNotificationWithoutEventWaitUntil) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -636,9 +593,6 @@
 #endif
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, HasPermissionSaysDefault) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -649,9 +603,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, HasPermissionSaysGranted) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -669,9 +620,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, HasPermissionSaysDenied) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -690,9 +638,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnregisterSuccess) {
-  if (!IsPushSupported())
-    return;
-
   std::string script_result;
 
   EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result));
@@ -734,19 +679,6 @@
   EXPECT_EQ("unregister result: false", script_result);
 }
 
-#if defined(OS_ANDROID)
-IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushUnavailableOnAndroidICS) {
-  // This test should only run on Android ICS to confirm that the Push API
-  // is not available on that version of Android.
-  if (IsPushSupported())
-    return;
-
-  std::string script_result;
-  ASSERT_TRUE(RunScript("window.PushManager", &script_result));
-  EXPECT_EQ("undefined", script_result);
-}
-#endif
-
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        GlobalResetPushPermissionUnregisters) {
   std::string script_result;
diff --git a/chrome/browser/resources/bookmark_manager/js/main.js b/chrome/browser/resources/bookmark_manager/js/main.js
index efbac59..acfc3d7d 100644
--- a/chrome/browser/resources/bookmark_manager/js/main.js
+++ b/chrome/browser/resources/bookmark_manager/js/main.js
@@ -1029,13 +1029,13 @@
   performGlobalUndo = null;  // This can't be undone, so disable global undo.
 
   var parentId = computeParentFolderForNewItem();
-  var selectedItem = bmm.list.selectedItem;
+  var selectedItems = bmm.list.selectedItems;
   var newIndex;
   // Callback is called after tree and list data model updated.
   function createFolder(callback) {
-    if (selectedItem && document.activeElement != bmm.tree &&
-        !bmm.isFolder(selectedItem) && selectedItem.id != 'new') {
-      newIndex = bmm.list.dataModel.indexOf(selectedItem) + 1;
+    if (selectedItems.length == 1 && document.activeElement != bmm.tree &&
+        !bmm.isFolder(selectedItems[0]) && selectedItems[0].id != 'new') {
+      newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1;
     }
     chrome.bookmarks.create({
       title: loadTimeData.getString('new_folder_name'),
@@ -1086,12 +1086,12 @@
  */
 function addPage() {
   var parentId = computeParentFolderForNewItem();
-  var selectedItem = bmm.list.selectedItem;
+  var selectedItems = bmm.list.selectedItems;
   var newIndex;
   function editNewBookmark() {
-    if (selectedItem && document.activeElement != bmm.tree &&
-        !bmm.isFolder(selectedItem)) {
-      newIndex = bmm.list.dataModel.indexOf(selectedItem) + 1;
+    if (selectedItems.length == 1 && document.activeElement != bmm.tree &&
+        !bmm.isFolder(selectedItems[0])) {
+      newIndex = bmm.list.dataModel.indexOf(selectedItems[0]) + 1;
     }
 
     var fakeNode = {
diff --git a/chrome/browser/resources/chromeos/input_method/google_input_tools_manifest.json b/chrome/browser/resources/chromeos/input_method/google_input_tools_manifest.json
index cf52fa28a..d8595ed 100644
--- a/chrome/browser/resources/chromeos/input_method/google_input_tools_manifest.json
+++ b/chrome/browser/resources/chromeos/input_method/google_input_tools_manifest.json
@@ -148,7 +148,7 @@
       "name": "__MSG_inputmethod_zhuyin__",
       "type": "ime",
       "id": "zh-hant-t-i0-und",
-      "indicator": "\u9177",
+      "indicator": "\u6CE8",
       "description": "Zhuyin",
       "language": [
         "zh-TW",
diff --git a/chrome/browser/resources/chromeos/login/custom_elements.html b/chrome/browser/resources/chromeos/login/custom_elements.html
index 3cd18cd..c043cd5 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements.html
@@ -4,5 +4,6 @@
 <include src="controller-pairing-screen.html">
 <include src="host-pairing-screen.html">
 <include src="throbber_notice.html">
+<include src="notification_card.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements.js b/chrome/browser/resources/chromeos/login/custom_elements.js
index 587b93f..b5f32a0 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements.js
@@ -7,3 +7,4 @@
 <include src="indeterminate-progress.js">
 <include src="controller-pairing-screen.js">
 <include src="host-pairing-screen.js">
+<include src="notification_card.js">
diff --git a/chrome/browser/resources/chromeos/login/gaia_whiteist_error.js b/chrome/browser/resources/chromeos/login/gaia_whiteist_error.js
deleted file mode 100644
index eed9092..0000000
--- a/chrome/browser/resources/chromeos/login/gaia_whiteist_error.js
+++ /dev/null
@@ -1,20 +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.
-
-Polymer('gaia-whitelist-error', (function() {
-  // The help topic regarding user not being in the whitelist.
-  /** @const */ var HELP_CANT_ACCESS_ACCOUNT = 188036;
-
-  return {
-    enterpriseManaged: false,
-
-    onLearnMoreClicked: function() {
-      chrome.send('launchHelpApp', [HELP_CANT_ACCESS_ACCOUNT]);
-    },
-
-    tryAgainButtonClicked: function() {
-      $('gaia-signin').showWhitelistCheckFailedError(false);
-    }
-  };
-})());
diff --git a/chrome/browser/resources/chromeos/login/gaia_whitelist_error.html b/chrome/browser/resources/chromeos/login/gaia_whitelist_error.html
deleted file mode 100644
index b8c2a3cf..0000000
--- a/chrome/browser/resources/chromeos/login/gaia_whitelist_error.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
-<link rel="import" href="chrome://resources/polymer/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/core-icons/core-icons.html">
-
-<polymer-element name="gaia-whitelist-error" attributes="errorMsg
-    enterpriseErrorMsg tryAgainBtn learnMoreBtn">
-  <template>
-    <style>
-      #container {
-        padding: 40px;
-      }
-
-      #icon-container {
-        margin-bottom: 14px;
-      }
-
-      core-icon {
-        color: #fbc02d;
-        height: 28px;
-        width: 28px;
-      }
-
-      #text-container {
-        color: grey;
-        line-height: 130%;
-        text-align: center;
-        width: 240px;
-      }
-
-      #controls-container {
-        width: 100%;
-      }
-
-      .learn-more-button {
-        color: rgb(66, 133, 244);
-        text-transform: none;
-      }
-
-      .try-again-button {
-        background-color: rgb(66, 133, 244);
-        color: rgb(255, 255, 255);
-        width: 126px;
-      }
-    </style>
-    <div id="container" vertical layout center fit>
-      <div flex layout center-justified vertical>
-        <div id="icon-container" vertical layout center>
-          <core-icon icon="warning"></core-icon>
-        </div>
-        <div id="text-container">
-          <div id="error-msg" hidden?="{{enterpriseManaged}}">
-            {{errorMsg}}
-          </div>
-          <div id="enterprise-error-msg" hidden?="{{!enterpriseManaged}}">
-            {{enterpriseErrorMsg}}
-          </div>
-        </div>
-      </div>
-      <div id="controls-container" horizontal layout justified
-          center>
-        <paper-button class="learn-more-button"
-            on-tap={{onLearnMoreClicked}}>
-          {{learnMoreBtn}}
-        </paper-button>
-        <paper-button class="try-again-button" raised
-            on-tap="{{tryAgainButtonClicked}}">
-          {{tryAgainBtn}}
-        </paper-button>
-      </div>
-    </div>
-  </template>
-</polymer-element>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/login.html b/chrome/browser/resources/chromeos/login/login.html
index fa1e864..d51906c 100644
--- a/chrome/browser/resources/chromeos/login/login.html
+++ b/chrome/browser/resources/chromeos/login/login.html
@@ -9,7 +9,7 @@
 <title i18n-content="title"></title>
 <include src="login_resources.html">
 <include src="throbber_notice.html">
-<include src="gaia_whitelist_error.html">
+<include src="notification_card.html">
 <script src="chrome://oobe/login.js"></script>
 <script src="chrome://oobe/gaia_auth_host.js"></script>
 </head>
diff --git a/chrome/browser/resources/chromeos/login/login.js b/chrome/browser/resources/chromeos/login/login.js
index 7f9ac0a0..ef3e6f0 100644
--- a/chrome/browser/resources/chromeos/login/login.js
+++ b/chrome/browser/resources/chromeos/login/login.js
@@ -7,7 +7,7 @@
  */
 
 <include src="login_common.js">
-<include src="gaia_whiteist_error.js">
+<include src="notification_card.js">
 
 cr.define('cr.ui.Oobe', function() {
   return {
diff --git a/chrome/browser/resources/chromeos/login/notification_card.css b/chrome/browser/resources/chromeos/login/notification_card.css
new file mode 100644
index 0000000..7a728f3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/notification_card.css
@@ -0,0 +1,50 @@
+/* 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.
+ */
+
+#container {
+  padding: 40px;
+}
+
+#icon-container {
+  margin-bottom: 14px;
+}
+
+/* ':host' and '[icon]' are needed to increase selector's specificity. */
+:host core-icon[icon] {
+  height: 28px;
+  width: 28px;
+}
+
+core-icon[icon=warning] {
+  color: rgb(251, 192, 45);
+}
+
+core-icon[icon=done] {
+  color: rgb(15, 157, 88);
+}
+
+#heading {
+  font-size: 20px;
+  margin-bottom: 14px;
+}
+
+#text-container {
+  color: grey;
+  line-height: 130%;
+  max-width: 240px;
+  text-align: center;
+}
+
+paper-button {
+  background-color: rgb(66, 133, 244);
+  color: rgb(255, 255, 255);
+  min-width: 126px;
+}
+
+a {
+  color: rgb(66, 133, 244);
+  text-decoration: none;
+}
+
diff --git a/chrome/browser/resources/chromeos/login/notification_card.html b/chrome/browser/resources/chromeos/login/notification_card.html
new file mode 100644
index 0000000..34a4a328
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/notification_card.html
@@ -0,0 +1,61 @@
+<!-- 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. -->
+
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="chrome://resources/polymer/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/core-icons/core-icons.html">
+
+<!--
+  A simple notification card with a button, link (optional) and icon (optional).
+  Example:
+    <notification-card buttonLabel="OK" linkLabel="What?" heading="Hello!"
+        type="success">
+      Great success!
+    </notification-card>
+
+  Atributes:
+   'buttonLabel' - label displayed on the button.
+   'linkLabel' - text of the link. If empty or not set, the link is hidden.
+   'heading' - heading. Can be omitted.
+   'type' - icon type. Can be either 'success' or 'fail'. If not set, no icon
+            is displayed.
+
+  Events:
+    'buttonclick' - fired on button click.
+    'linkclick' - fired on link click.
+
+-->
+<polymer-element name="notification-card"
+    attributes="buttonLabel linkLabel heading type">
+  <template>
+    <link rel="stylesheet" href="notification_card.css">
+
+    <div id="container" vertical layout center fit>
+      <div flex vertical layout center center-justified>
+        <div id="icon-container" vertical layout center hidden?="{{!type}}">
+          <template if="{{type == 'fail'}}">
+            <core-icon icon="warning"></core-icon>
+          </template>
+          <template if="{{type == 'success'}}">
+            <core-icon icon="done"></core-icon>
+          </template>
+        </div>
+        <div id="heading" hidden?="{{!heading}}">
+          {{heading}}
+        </div>
+        <div id="text-container">
+          <content></content>
+        </div>
+      </div>
+      <div self-stretch horizontal reverse layout justified center>
+        <paper-button on-tap="{{buttonClicked}}">
+          {{buttonLabel}}
+        </paper-button>
+        <a href="#" on-click="{{linkClicked}}" hidden?="{{!linkLabel}}">
+          {{linkLabel}}
+        </a>
+      </div>
+    </div>
+  </template>
+</polymer-element>
diff --git a/chrome/browser/resources/chromeos/login/notification_card.js b/chrome/browser/resources/chromeos/login/notification_card.js
new file mode 100644
index 0000000..edc91a8
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/notification_card.js
@@ -0,0 +1,16 @@
+// 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.
+
+Polymer('notification-card', (function() {
+  return {
+    buttonClicked: function() {
+      this.fire('buttonclick');
+    },
+
+    linkClicked: function(e) {
+      this.fire('linkclick');
+      e.preventDefault();
+    }
+  };
+})());
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index 93f80843..218a7d9 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -31,13 +31,10 @@
     <div class="throbber"></div>
     <throbber-notice i18n-values="text:gaiaLoadingNewGaia"></throbber-notice>
   </div>
-  <gaia-whitelist-error
-      i18n-values="errorMsg:whitelistErrorConsumer;
-                   enterpriseErrorMsg:whitelistErrorEnterprise;
-                   tryAgainBtn:tryAgainButton;
-                   learnMoreBtn:learnMoreButton"
-      id="gaia-whitelist-error">
-  </gaia-whitelist-error>
+  <notification-card id="gaia-whitelist-error" type="fail"
+      i18n-values="buttonLabel:tryAgainButton;
+                   linkLabel:learnMoreButton">
+  </notification-card>
   <div id="enterprise-info-container" hidden>
     <include src="enterprise_info.html">
   </div>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index e6ed735..0ae93ea3 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -16,6 +16,9 @@
 
   /** @const */ var HELP_TOPIC_ENTERPRISE_REPORTING = 2535613;
 
+  // The help topic regarding user not being in the whitelist.
+  /** @const */ var HELP_CANT_ACCESS_ACCOUNT = 188036;
+
   return {
     EXTERNAL_API: [
       'loadAuthExtension',
@@ -151,12 +154,21 @@
         $('back-button-item').hidden = true;
         $('signin-frame').back();
         e.preventDefault();
-      }.bind(this));
+      });
+
       $('close-button-item').addEventListener('click', function(e) {
         this.cancel();
         e.preventDefault();
       }.bind(this));
 
+      $('gaia-whitelist-error').addEventListener('buttonclick', function() {
+         this.showWhitelistCheckFailedError(false);
+      }.bind(this));
+
+      $('gaia-whitelist-error').addEventListener('linkclick', function() {
+        chrome.send('launchHelpApp', [HELP_CANT_ACCESS_ACCOUNT]);
+      });
+
       this.updateLocalizedContent();
     },
 
@@ -793,9 +805,11 @@
      * @param {!Object} opt_data Optional additional information.
      */
     showWhitelistCheckFailedError: function(show, opt_data) {
-      if (opt_data) {
-        $('gaia-whitelist-error').enterpriseManaged =
-            opt_data.enterpriseManaged;
+      if (show) {
+        var isManaged = opt_data && opt_data.enterpriseManaged;
+        $('gaia-whitelist-error').textContent =
+            loadTimeData.getValue(isManaged ? 'whitelistErrorEnterprise' :
+                                              'whitelistErrorConsumer');
       }
 
       this.classList.toggle('whitelist-error', show);
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/constants.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/constants.js
index 93de962..8001c16 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/constants.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/constants.js
@@ -5,14 +5,14 @@
 
 /** @const */ var Constants = {
   /**
-   * Key to access wallpaper rss in chrome.local.storage.
+   * Key to access wallpaper rss in chrome.storage.local.
    */
-  AccessRssKey: 'wallpaper-picker-surprise-rss-key',
+  AccessLocalRssKey: 'wallpaper-picker-surprise-rss-key',
 
   /**
    * Key to access wallpaper manifest in chrome.storage.local.
    */
-  AccessManifestKey: 'wallpaper-picker-manifest-key',
+  AccessLocalManifestKey: 'wallpaper-picker-manifest-key',
 
   /**
    * Key to access user wallpaper info in chrome.storage.local.
@@ -25,15 +25,22 @@
   AccessSyncWallpaperInfoKey: 'wallpaper-sync-info-key',
 
   /**
-   * Key to access last changed date of a surprise wallpaper.
+   * Key to access last changed date of a surprise wallpaper in
+   * chrome.storage.local or chrome.storage.sync.
    */
   AccessLastSurpriseWallpaperChangedDate: 'wallpaper-last-changed-date-key',
 
   /**
    * Key to access if surprise me feature is enabled or not in
-   * chrome.local.storage.
+   * chrome.storage.local.
    */
-  AccessSurpriseMeEnabledKey: 'surprise-me-enabled-key',
+  AccessLocalSurpriseMeEnabledKey: 'surprise-me-enabled-key',
+
+  /**
+   * Key to access if surprise me feature is enabled or not in
+   * chrome.storage.sync.
+   */
+  AccessSyncSurpriseMeEnabledKey: 'sync-surprise-me-enabled-key',
 
   /**
    * Suffix to append to baseURL if requesting high resoultion wallpaper.
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 738f799..ec17c1fe 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/event_page.js
@@ -40,8 +40,8 @@
   // wallpaper picker. If any other error occurs, proceed with local copy of
   // rss.
   WallpaperUtil.fetchURL(Constants.WallpaperRssURL, 'document', function(xhr) {
-    WallpaperUtil.saveToStorage(Constants.AccessRssKey,
-        new XMLSerializer().serializeToString(xhr.responseXML), false);
+    WallpaperUtil.saveToLocalStorage(Constants.AccessLocalRssKey,
+        new XMLSerializer().serializeToString(xhr.responseXML));
     self.updateSurpriseWallpaper(xhr.responseXML);
   }, onFailure);
 };
@@ -62,8 +62,9 @@
  */
 SurpriseWallpaper.prototype.fallbackToLocalRss_ = function() {
   var self = this;
-  Constants.WallpaperLocalStorage.get(Constants.AccessRssKey, function(items) {
-    var rssString = items[Constants.AccessRssKey];
+  Constants.WallpaperLocalStorage.get(Constants.AccessLocalRssKey,
+      function(items) {
+    var rssString = items[Constants.AccessLocalRssKey];
     if (rssString) {
       self.updateSurpriseWallpaper(new DOMParser().parseFromString(rssString,
                                                                    'text/xml'));
@@ -119,13 +120,21 @@
  */
 SurpriseWallpaper.prototype.updateRandomWallpaper_ = function() {
   var self = this;
-  Constants.WallpaperSyncStorage.get(
-      Constants.AccessLastSurpriseWallpaperChangedDate, function(items) {
+  var onSuccess = function(items) {
     var dateString = new Date().toDateString();
     // At most one random wallpaper per day.
     if (items[Constants.AccessLastSurpriseWallpaperChangedDate] != dateString) {
       self.setRandomWallpaper_(dateString);
     }
+  };
+  WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+    if (syncEnabled) {
+      Constants.WallpaperSyncStorage.get(
+          Constants.AccessLastSurpriseWallpaperChangedDate, onSuccess);
+    } else {
+      Constants.WallpaperLocalStorage.get(
+          Constants.AccessLastSurpriseWallpaperChangedDate, onSuccess);
+    }
   });
 };
 
@@ -138,9 +147,9 @@
  */
 SurpriseWallpaper.prototype.setRandomWallpaper_ = function(dateString) {
   var self = this;
-  Constants.WallpaperLocalStorage.get(Constants.AccessManifestKey,
+  Constants.WallpaperLocalStorage.get(Constants.AccessLocalManifestKey,
                                       function(items) {
-    var manifest = items[Constants.AccessManifestKey];
+    var manifest = items[Constants.AccessLocalManifestKey];
     if (manifest && manifest.wallpaper_list) {
       var filtered = manifest.wallpaper_list.filter(function(element) {
         // Older version manifest do not have available_for_surprise_me field.
@@ -154,10 +163,13 @@
       var onSuccess = function() {
         WallpaperUtil.saveWallpaperInfo(wallpaperURL, wallpaper.default_layout,
             Constants.WallpaperSourceEnum.Online);
-        WallpaperUtil.saveToStorage(
+        WallpaperUtil.saveToLocalStorage(
             Constants.AccessLastSurpriseWallpaperChangedDate,
-            dateString,
-            true);
+            dateString, function() {
+              WallpaperUtil.saveToSyncStorage(
+                Constants.AccessLastSurpriseWallpaperChangedDate,
+                dateString);
+            });
       };
       WallpaperUtil.setOnlineWallpaper(wallpaperURL, wallpaper.default_layout,
           onSuccess, self.retryLater_.bind(self));
@@ -188,8 +200,12 @@
       WallpaperUtil.saveWallpaperInfo(url, layout,
                                       Constants.WallpaperSourceEnum.Online);
       var dateString = new Date().toDateString();
-      WallpaperUtil.saveToStorage(
-          Constants.AccessLastSurpriseWallpaperChangedDate, dateString, true);
+      WallpaperUtil.saveToLocalStorage(
+          Constants.AccessLastSurpriseWallpaperChangedDate,
+          dateString, function() {
+            WallpaperUtil.saveToSyncStorage(
+              Constants.AccessLastSurpriseWallpaperChangedDate, dataString);
+          });
     } else {
       self.updateRandomWallpaper_();
     }
@@ -202,8 +218,11 @@
 SurpriseWallpaper.prototype.disable = function() {
   chrome.alarms.clearAll();
   // Makes last changed date invalid.
-  WallpaperUtil.saveToStorage(Constants.AccessLastSurpriseWallpaperChangedDate,
-                              '', true);
+  WallpaperUtil.saveToLocalStorage(
+      Constants.AccessLastSurpriseWallpaperChangedDate, '', function() {
+    WallpaperUtil.saveToSyncStorage(
+      Constants.AccessLastSurpriseWallpaperChangedDate, '');
+  });
 };
 
 /**
@@ -250,7 +269,9 @@
 });
 
 chrome.syncFileSystem.onFileStatusChanged.addListener(function(detail) {
-  WallpaperUtil.enabledSyncThemesCallback(function() {
+  WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+    if (!syncEnabled)
+      return;
     if (detail.status == 'synced' &&
         detail.direction == 'remote_to_local') {
       if (detail.action == 'added') {
@@ -279,69 +300,85 @@
 });
 
 chrome.storage.onChanged.addListener(function(changes, namespace) {
-  WallpaperUtil.enabledSyncThemesCallback(function() {
-    WallpaperUtil.requestSyncFS(function() {});
-    if (changes[Constants.AccessSurpriseMeEnabledKey]) {
-      if (changes[Constants.AccessSurpriseMeEnabledKey].newValue) {
-        SurpriseWallpaper.getInstance().next();
-      } else {
-        SurpriseWallpaper.getInstance().disable();
+  WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+    if (syncEnabled) {
+      // If sync theme is enabled, use values from chrome.storage.sync to sync
+      // wallpaper changes.
+      WallpaperUtil.requestSyncFS(function() {});
+      if (changes[Constants.AccessSyncSurpriseMeEnabledKey]) {
+        if (changes[Constants.AccessSyncSurpriseMeEnabledKey].newValue) {
+          SurpriseWallpaper.getInstance().next();
+        } else {
+          SurpriseWallpaper.getInstance().disable();
+        }
       }
-    }
 
-    if (changes[Constants.AccessSyncWallpaperInfoKey]) {
-      var syncInfo = changes[Constants.AccessSyncWallpaperInfoKey].newValue;
-
-      Constants.WallpaperSyncStorage.get(Constants.AccessSurpriseMeEnabledKey,
-                                         function(enabledItems) {
-        var syncSurpriseMeEnabled =
-            enabledItems[Constants.AccessSurpriseMeEnabledKey];
+      if (changes[Constants.AccessSyncWallpaperInfoKey]) {
+        var syncInfo = changes[Constants.AccessSyncWallpaperInfoKey].newValue;
 
         Constants.WallpaperSyncStorage.get(
-            Constants.AccessLastSurpriseWallpaperChangedDate, function(items) {
-          var syncLastSurpriseMeChangedDate =
-              items[Constants.AccessLastSurpriseWallpaperChangedDate];
+            Constants.AccessSyncSurpriseMeEnabledKey, function(enabledItems) {
+          var syncSurpriseMeEnabled =
+              enabledItems[Constants.AccessSyncSurpriseMeEnabledKey];
 
-          var today = new Date().toDateString();
-          // If SurpriseMe is enabled and surprise wallpaper hasn't been changed
-          // today, we should not sync the change, instead onAlarm() will be
-          // triggered to update a surprise me wallpaper.
-          if (!syncSurpriseMeEnabled ||
-              (syncSurpriseMeEnabled &&
-                  syncLastSurpriseMeChangedDate == today)) {
-            Constants.WallpaperLocalStorage.get(
-                Constants.AccessLocalWallpaperInfoKey, function(infoItems) {
-              var localInfo = infoItems[Constants.AccessLocalWallpaperInfoKey];
-              // Normally, the wallpaper info saved in local storage and sync
-              // storage are the same. If the synced value changed by sync
-              // service, they may different. In that case, change wallpaper to
-              // the one saved in sync storage and update the local value.
-              if (localInfo == undefined ||
-                  localInfo.url != syncInfo.url ||
-                  localInfo.layout != syncInfo.layout ||
-                  localInfo.source != syncInfo.source) {
-                if (syncInfo.source == Constants.WallpaperSourceEnum.Online) {
-                  // TODO(bshe): Consider schedule an alarm to set online
-                  // wallpaper later when failed. Note that we need to cancel
-                  // the retry if user set another wallpaper before retry alarm
-                  // invoked.
-                  WallpaperUtil.setOnlineWallpaper(syncInfo.url,
-                      syncInfo.layout, function() {}, function() {});
-                } else if (syncInfo.source ==
-                              Constants.WallpaperSourceEnum.Custom) {
-                  WallpaperUtil.setCustomWallpaperFromSyncFS(syncInfo.url,
-                                                             syncInfo.layout);
-                } else if (syncInfo.source ==
+          Constants.WallpaperSyncStorage.get(
+              Constants.AccessLastSurpriseWallpaperChangedDate,
+              function(items) {
+            var syncLastSurpriseMeChangedDate =
+                items[Constants.AccessLastSurpriseWallpaperChangedDate];
+
+            var today = new Date().toDateString();
+            // If SurpriseMe is enabled and surprise wallpaper hasn't been
+            // changed today, we should not sync the change, instead onAlarm()
+            // will be triggered to update a surprise me wallpaper.
+            if (!syncSurpriseMeEnabled ||
+                (syncSurpriseMeEnabled &&
+                 syncLastSurpriseMeChangedDate == today)) {
+              Constants.WallpaperLocalStorage.get(
+                  Constants.AccessLocalWallpaperInfoKey, function(infoItems) {
+                var localInfo =
+                    infoItems[Constants.AccessLocalWallpaperInfoKey];
+                // Normally, the wallpaper info saved in local storage and sync
+                // storage are the same. If the synced value changed by sync
+                // service, they may different. In that case, change wallpaper
+                // to the one saved in sync storage and update the local value.
+                if (localInfo == undefined ||
+                    localInfo.url != syncInfo.url ||
+                    localInfo.layout != syncInfo.layout ||
+                    localInfo.source != syncInfo.source) {
+                  if (syncInfo.source == Constants.WallpaperSourceEnum.Online) {
+                    // TODO(bshe): Consider schedule an alarm to set online
+                    // wallpaper later when failed. Note that we need to cancel
+                    // the retry if user set another wallpaper before retry
+                    // alarm invoked.
+                    WallpaperUtil.setOnlineWallpaper(syncInfo.url,
+                        syncInfo.layout, function() {}, function() {});
+                  } else if (syncInfo.source ==
+                             Constants.WallpaperSourceEnum.Custom) {
+                    WallpaperUtil.setCustomWallpaperFromSyncFS(syncInfo.url,
+                                                               syncInfo.layout);
+                  } else if (syncInfo.source ==
                               Constants.WallpaperSourceEnum.Default) {
-                  chrome.wallpaperPrivate.resetWallpaper();
+                    chrome.wallpaperPrivate.resetWallpaper();
+                  }
+                  WallpaperUtil.saveToLocalStorage(
+                      Constants.AccessLocalWallpaperInfoKey, syncInfo);
                 }
-                WallpaperUtil.saveToStorage(
-                    Constants.AccessLocalWallpaperInfoKey, syncInfo, false);
-              }
-            });
-          }
+              });
+            }
+          });
         });
-      });
+      }
+    } else {
+      // If sync theme is disabled, use values from chrome.storage.local to
+      // track wallpaper changes.
+      if (changes[Constants.AccessLocalSurpriseMeEnabledKey]) {
+        if (changes[Constants.AccessLocalSurpriseMeEnabledKey].newValue) {
+          SurpriseWallpaper.getInstance().next();
+        } else {
+          SurpriseWallpaper.getInstance().disable();
+        }
+      }
     }
   });
 });
@@ -351,7 +388,10 @@
 });
 
 chrome.wallpaperPrivate.onWallpaperChangedBy3rdParty.addListener(function() {
-  WallpaperUtil.saveToStorage(Constants.AccessSurpriseMeEnabledKey,
-                              false, true);
+  WallpaperUtil.saveToLocalStorage(
+      Constants.AccessLocalSurpriseMeEnabledKey, false, function() {
+    WallpaperUtil.saveToSyncStorage(Constants.AccessSyncSurpriseMeEnabledKey,
+                                    false);
+  });
   SurpriseWallpaper.getInstance().disable();
 });
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js
index 3e01d55..de8c5187 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/util.js
@@ -89,14 +89,12 @@
 };
 
 /**
- * Executes callback if sync theme is enabled.
- * @param {function} callback The callback will be executed if sync themes is
- *     enabled.
+ * Executes callback after requesting the sync settings.
+ * @param {function} callback The callback will be executed.
  */
 WallpaperUtil.enabledSyncThemesCallback = function(callback) {
   chrome.wallpaperPrivate.getSyncSetting(function(setting) {
-    if (setting.syncThemes)
-      callback();
+    callback(setting.syncThemes);
   });
 };
 
@@ -106,7 +104,9 @@
  *     handler is available.
  */
 WallpaperUtil.requestSyncFS = function(callback) {
-  WallpaperUtil.enabledSyncThemesCallback(function() {
+  WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+    if (!syncEnabled)
+      return;
     if (WallpaperUtil.syncFs) {
       callback(WallpaperUtil.syncFs);
     } else {
@@ -264,18 +264,28 @@
  * Saves value to local storage that associates with key.
  * @param {string} key The key that associates with value.
  * @param {string} value The value to save to local storage.
- * @param {boolen} sync True if the value is saved to sync storage.
- * @param {function=} opt_callback The callback on success, or on failure.
+ * @param {function=} opt_callback The callback on success.
  */
-WallpaperUtil.saveToStorage = function(key, value, sync, opt_callback) {
+WallpaperUtil.saveToLocalStorage = function(key, value, opt_callback) {
   var items = {};
   items[key] = value;
-  if (sync)
-    WallpaperUtil.enabledSyncThemesCallback(function() {
+  Constants.WallpaperLocalStorage.set(items, opt_callback);
+};
+
+/**
+ * Saves value to sync storage that associates with key if sync theme is
+ * enabled.
+ * @param {string} key The key that associates with value.
+ * @param {string} value The value to save to sync storage.
+ * @param {function=} opt_callback The callback on success.
+ */
+WallpaperUtil.saveToSyncStorage = function(key, value, opt_callback) {
+  var items = {};
+  items[key] = value;
+  WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+    if (syncEnabled)
       Constants.WallpaperSyncStorage.set(items, opt_callback);
-    });
-  else
-    Constants.WallpaperLocalStorage.set(items, opt_callback);
+  });
 };
 
 /**
@@ -292,10 +302,10 @@
       layout: layout,
       source: source
   };
-  WallpaperUtil.saveToStorage(Constants.AccessLocalWallpaperInfoKey,
-                              wallpaperInfo, false, function() {
-    WallpaperUtil.saveToStorage(Constants.AccessSyncWallpaperInfoKey,
-                                wallpaperInfo, true);
+  WallpaperUtil.saveToLocalStorage(Constants.AccessLocalWallpaperInfoKey,
+                              wallpaperInfo, function() {
+    WallpaperUtil.saveToSyncStorage(Constants.AccessSyncWallpaperInfoKey,
+                                wallpaperInfo);
   });
 };
 
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
index 1a46792..f490701 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
@@ -171,14 +171,15 @@
    */
   WallpaperManager.prototype.onLoadManifestSuccess_ = function(manifest) {
     this.manifest_ = manifest;
-    WallpaperUtil.saveToStorage(Constants.AccessManifestKey, manifest, false);
+    WallpaperUtil.saveToLocalStorage(Constants.AccessLocalManifestKey,
+                                     manifest);
     this.postManifestDomInit_();
   };
 
   // Sets manifest to previously saved object if any and shows connection error.
   // Called after manifest failed to load.
   WallpaperManager.prototype.onLoadManifestFailed_ = function() {
-    var accessManifestKey = Constants.AccessManifestKey;
+    var accessManifestKey = Constants.AccessLocalManifestKey;
     var self = this;
     Constants.WallpaperLocalStorage.get(accessManifestKey, function(items) {
       self.manifest_ = items[accessManifestKey] ?
@@ -198,29 +199,40 @@
     var self = this;
     var checkbox = $('surprise-me').querySelector('#checkbox');
     var shouldEnable = !checkbox.classList.contains('checked');
-    WallpaperUtil.saveToStorage(Constants.AccessSurpriseMeEnabledKey,
-                                shouldEnable, true, function() {
+    var onSuccess = function() {
       if (chrome.runtime.lastError == null) {
-          if (shouldEnable) {
-            checkbox.classList.add('checked');
-            // Hides the wallpaper set by message if there is any.
-            $('wallpaper-set-by-message').textContent = '';
-          } else {
-            // Unchecking the "Surprise me" checkbox falls back to the previous
-            // wallpaper before "Surprise me" was turned on.
-            if (self.wallpaperGrid_.activeItem) {
-              self.setSelectedWallpaper_(self.wallpaperGrid_.activeItem);
-              self.onWallpaperChanged_(self.wallpaperGrid_.activeItem,
-                                       self.currentWallpaper_);
-            }
-            checkbox.classList.remove('checked');
-          }
-          $('categories-list').disabled = shouldEnable;
-          $('wallpaper-grid').disabled = shouldEnable;
+        if (shouldEnable) {
+          checkbox.classList.add('checked');
+          // Hides the wallpaper set by message if there is any.
+          $('wallpaper-set-by-message').textContent = '';
         } else {
-          // TODO(bshe): show error message to user.
-          console.error('Failed to save surprise me option to chrome storage.');
+          // Unchecking the "Surprise me" checkbox falls back to the previous
+          // wallpaper before "Surprise me" was turned on.
+          if (self.wallpaperGrid_.activeItem) {
+            self.setSelectedWallpaper_(self.wallpaperGrid_.activeItem);
+            self.onWallpaperChanged_(self.wallpaperGrid_.activeItem,
+                                     self.currentWallpaper_);
+          }
+          checkbox.classList.remove('checked');
         }
+        $('categories-list').disabled = shouldEnable;
+        $('wallpaper-grid').disabled = shouldEnable;
+      } else {
+        // TODO(bshe): show error message to user.
+        console.error('Failed to save surprise me option to chrome storage.');
+      }
+    };
+
+    // To prevent the onChanged event being fired twice, we only save the value
+    // to sync storage if the sync theme is enabled, otherwise save it to local
+    // storage.
+    WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
+      if (syncEnabled)
+        WallpaperUtil.saveToSyncStorage(
+            Constants.AccessSyncSurpriseMeEnabledKey, shouldEnable, onSuccess);
+      else
+        WallpaperUtil.saveToLocalStorage(
+            Constants.AccessLocalSurpriseMeEnabledKey, shouldEnable, onSuccess);
     });
   };
 
@@ -272,29 +284,49 @@
       $('surprise-me').hidden = false;
       $('surprise-me').addEventListener('click',
                                         this.toggleSurpriseMe_.bind(this));
-      Constants.WallpaperSyncStorage.get(Constants.AccessSurpriseMeEnabledKey,
-                                          function(items) {
+      var onSurpriseMeEnabled = function() {
+        $('surprise-me').querySelector('#checkbox').classList.add('checked');
+        $('categories-list').disabled = true;
+        $('wallpaper-grid').disabled = true;
+      };
+
+      WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
         // Surprise me has been moved from local to sync storage, prefer
         // values from sync, but if unset check local and update synced pref
         // if applicable.
-        if (!items.hasOwnProperty(Constants.AccessSurpriseMeEnabledKey)) {
-          Constants.WallpaperLocalStorage.get(
-              Constants.AccessSurpriseMeEnabledKey, function(values) {
-            if (values.hasOwnProperty(Constants.AccessSurpriseMeEnabledKey)) {
-              WallpaperUtil.saveToStorage(Constants.AccessSurpriseMeEnabledKey,
-                  values[Constants.AccessSurpriseMeEnabledKey], true);
-            }
-            if (values[Constants.AccessSurpriseMeEnabledKey]) {
-                $('surprise-me').querySelector('#checkbox').classList.add(
-                    'checked');
-                $('categories-list').disabled = true;
-                $('wallpaper-grid').disabled = true;
+        if (syncEnabled) {
+          Constants.WallpaperSyncStorage.get(
+              Constants.AccessSyncSurpriseMeEnabledKey, function(items) {
+            if (items.hasOwnProperty(
+                Constants.AccessSyncSurpriseMeEnabledKey)) {
+              if (items[Constants.AccessSyncSurpriseMeEnabledKey]) {
+                onSurpriseMeEnabled();
+              }
+            } else {
+              Constants.WallpaperLocalStorage.get(
+                  Constants.AccessLocalSurpriseMeEnabledKey, function(items) {
+                if (items.hasOwnProperty(
+                    Constants.AccessLocalSurpriseMeEnabledKey)) {
+                  WallpaperUtil.saveToSyncStorage(
+                      Constants.AccessSyncSurpriseMeEnabledKey,
+                      items[Constants.AccessLocalSurpriseMeEnabledKey]);
+                  if (items[Constants.AccessLocalSurpriseMeEnabledKey]) {
+                    onSurpriseMeEnabled();
+                  }
+                }
+              });
             }
           });
-        } else if (items[Constants.AccessSurpriseMeEnabledKey]) {
-          $('surprise-me').querySelector('#checkbox').classList.add('checked');
-          $('categories-list').disabled = true;
-          $('wallpaper-grid').disabled = true;
+        } else {
+          Constants.WallpaperLocalStorage.get(
+              Constants.AccessLocalSurpriseMeEnabledKey, function(items) {
+            if (items.hasOwnProperty(
+                Constants.AccessLocalSurpriseMeEnabledKey)) {
+              if (items[Constants.AccessLocalSurpriseMeEnabledKey]) {
+                onSurpriseMeEnabled();
+              }
+            }
+          });
         }
       });
 
@@ -808,8 +840,8 @@
                 self.wallpaperGrid_.dataModel.splice(0, 0, wallpaperInfo);
                 self.wallpaperGrid_.selectedItem = wallpaperInfo;
                 self.onWallpaperChanged_(wallpaperInfo, fileName);
-                WallpaperUtil.saveToStorage(self.currentWallpaper_, layout,
-                                            false);
+                WallpaperUtil.saveToLocalStorage(self.currentWallpaper_,
+                                                 layout);
               };
 
               fileWriter.onerror = errorHandler;
@@ -942,7 +974,7 @@
         self.removeCustomWallpaper(fileName);
         $('set-wallpaper-layout').disabled = true;
       } else {
-        WallpaperUtil.saveToStorage(self.currentWallpaper_, layout, false);
+        WallpaperUtil.saveToLocalStorage(self.currentWallpaper_, layout);
         self.onWallpaperChanged_(self.wallpaperGrid_.activeItem,
                                  self.currentWallpaper_);
       }
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 2cd8e6e..d5f5374 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -175,9 +175,6 @@
         <include name="IDR_PDF_PDF_SCRIPTING_API_JS" file="pdf/pdf_scripting_api.js" type="BINDATA" />
         <include name="IDR_PDF_ZOOM_MANAGER_JS" file="pdf/zoom_manager.js" type="BINDATA" />
         <include name="IDR_PDF_CONTENT_SCRIPT_JS" file="pdf/content_script.js" type="BINDATA" />
-        <if expr="is_macosx">
-          <include name="IDR_PDF_SCROLLBARS_CSS" file="pdf/scrollbars_mac.css" type="BINDATA" />
-        </if>
 
         <include name="IDR_PDF_VIEWER_BOOKMARK_CSS" file="pdf/elements/viewer-bookmark/viewer-bookmark.css" type="BINDATA" />
         <include name="IDR_PDF_VIEWER_BOOKMARK_HTML" file="pdf/elements/viewer-bookmark/viewer-bookmark.html" type="BINDATA" />
diff --git a/chrome/browser/resources/downloads/downloads.css b/chrome/browser/resources/downloads/downloads.css
index 2e72404..10c55ff 100644
--- a/chrome/browser/resources/downloads/downloads.css
+++ b/chrome/browser/resources/downloads/downloads.css
@@ -107,7 +107,11 @@
 }
 
 .title-area {
-  white-space: nowrap;
+  display: flex;
+}
+
+.title-area > * {
+  flex-shrink: 0;
 }
 
 .name,
diff --git a/chrome/browser/resources/extensions/extension_error_overlay.js b/chrome/browser/resources/extensions/extension_error_overlay.js
index ca9a0fc6..b406b89 100644
--- a/chrome/browser/resources/extensions/extension_error_overlay.js
+++ b/chrome/browser/resources/extensions/extension_error_overlay.js
@@ -416,13 +416,15 @@
       }
 
       if (ExtensionErrorOverlay.canLoadFileSource(error.source, extensionUrl)) {
-        var relativeUrl = getRelativeUrl(error.source, extensionUrl);
+        // slice(1) because pathname starts with a /.
+        var pathname = new URL(error.source).pathname.slice(1);
 
+        // Use pathname instead of relativeUrl.
         var requestFileSourceArgs = {extensionId: error.extensionId,
                                      message: error.message,
-                                     pathSuffix: relativeUrl};
+                                     pathSuffix: pathname};
 
-        if (relativeUrl.toLowerCase() ==
+        if (pathname.toLowerCase() ==
                 ExtensionErrorOverlay.MANIFEST_FILENAME_) {
           requestFileSourceArgs.manifestKey = error.manifestKey;
           requestFileSourceArgs.manifestSpecific = error.manifestSpecific;
diff --git a/chrome/browser/resources/pdf/main.js b/chrome/browser/resources/pdf/main.js
index 6900c6dc..07fa2e0 100644
--- a/chrome/browser/resources/pdf/main.js
+++ b/chrome/browser/resources/pdf/main.js
@@ -42,18 +42,6 @@
 
   function generateStreamDetailsAndInitViewer() {
     var url = window.location.search.substring(1);
-
-    // Hack to enable custom scrollbars for print preview on non-retina mac
-    // displays. Remove after crbug.com/466039 is fixed.
-    if (url.indexOf(IS_MAC_PARAM) === 0) {
-      url = url.substring(IS_MAC_PARAM.length);
-      var link = document.createElement('link');
-      link.rel = 'stylesheet';
-      link.type = 'text/css';
-      link.href = 'scrollbars_mac.css';
-      document.getElementsByTagName('head')[0].appendChild(link);
-    }
-
     var streamDetails = {
       streamUrl: url,
       originalUrl: url,
diff --git a/chrome/browser/resources/pdf/pdf_scripting_api.js b/chrome/browser/resources/pdf/pdf_scripting_api.js
index fa570aee..84b94b12 100644
--- a/chrome/browser/resources/pdf/pdf_scripting_api.js
+++ b/chrome/browser/resources/pdf/pdf_scripting_api.js
@@ -250,8 +250,6 @@
   },
 };
 
-var IS_MAC_PARAM = 'isMac&';
-
 /**
  * Creates a PDF viewer with a scripting interface. This is basically 1) an
  * iframe which is navigated to the PDF viewer extension and 2) a scripting
@@ -262,11 +260,9 @@
  */
 function PDFCreateOutOfProcessPlugin(src) {
   var iframe = window.document.createElement('iframe');
-  var isMac = cr.isMac ? IS_MAC_PARAM : '';
   iframe.setAttribute(
       'src',
-      'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?' +
-      isMac + src);
+      'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?' + src);
   // Prevent the frame from being tab-focusable.
   iframe.setAttribute('tabindex', '-1');
   var client = new PDFScriptingAPI(window);
diff --git a/chrome/browser/resources/pdf/scrollbars_mac.css b/chrome/browser/resources/pdf/scrollbars_mac.css
deleted file mode 100644
index aaebda0..0000000
--- a/chrome/browser/resources/pdf/scrollbars_mac.css
+++ /dev/null
@@ -1,33 +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. */
-
-/*
- * Imitate mac scrollbars on non-retina displays to workaround crbug.com/466039.
- */
-@media
-(-webkit-max-device-pixel-ratio: 1) {
-
-::-webkit-scrollbar {
-  height: 14px;
-  width: 14px;
-}
-
-::-webkit-scrollbar-track,
-::-webkit-scrollbar-corner {
-  background-color: rgb(242,242,242);
-}
-
-::-webkit-scrollbar-thumb:vertical,
-::-webkit-scrollbar-thumb:horizontal {
-  -webkit-border-radius: 7px;
-  background-color: rgb(198,198,198);
-  border: 3px solid rgb(242,242,242);
-}
-
-::-webkit-scrollbar-thumb:vertical:hover,
-::-webkit-scrollbar-thumb:horizontal:hover {
-  background-color: rgb(126,126,126);
-}
-
-}
diff --git a/chrome/browser/resources/profiler/profiler.js b/chrome/browser/resources/profiler/profiler.js
index 9095872..fd7a65bd 100644
--- a/chrome/browser/resources/profiler/profiler.js
+++ b/chrome/browser/resources/profiler/profiler.js
@@ -1070,12 +1070,10 @@
         var linenumber = m[2];
 
         var link = addNode(td, 'a', filename + ' [' + linenumber + ']');
-        // http://chromesrc.appspot.com is a server I wrote specifically for
-        // this task. It redirects to the appropriate source file; the file
-        // paths given by the compiler can be pretty crazy and different
-        // between platforms.
-        link.href = 'http://chromesrc.appspot.com/?path=' +
-                    encodeURIComponent(filepath) + '&line=' + linenumber;
+
+        link.href = 'https://code.google.com/p/chromium/codesearch#search/&q=' +
+                    encodeURIComponent(filename) + ':' + linenumber +
+                    '&sq=package:chromium&type=cs';
         link.target = '_blank';
         return;
       }
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 173dff9..cf7a15a 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -11,8 +11,6 @@
     <link rel="stylesheet" href="chrome://md-settings/settings_page/settings_page.css">
     <link rel="stylesheet" href="a11y_page.css">
     <paper-shadow layout vertical cross-fade>
-      <cr-settings-page-header page="{{}}"></cr-settings-page-header>
-
       <div class="more-a11y-link">
         <a href="https://chrome.google.com/webstore/category/collection/accessibility"
             target="_blank" i18n-content="accessibilityMoreFeaturesLink"></a>
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.js b/chrome/browser/resources/settings/a11y_page/a11y_page.js
index 4181004..272c704 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.js
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.js
@@ -29,6 +29,15 @@
     prefs: null,
 
     /**
+     * Whether the page is a subpage.
+     *
+     * @attribute subpage
+     * @type boolean
+     * @default false
+     */
+    subpage: false,
+
+    /**
      * ID of the page.
      *
      * @attribute PAGE_ID
diff --git a/chrome/browser/resources/settings/date_time_page/date_time_page.html b/chrome/browser/resources/settings/date_time_page/date_time_page.html
index 2bc552a..a11f0d42 100644
--- a/chrome/browser/resources/settings/date_time_page/date_time_page.html
+++ b/chrome/browser/resources/settings/date_time_page/date_time_page.html
@@ -10,7 +10,6 @@
     <link rel="stylesheet" href="chrome://md-settings/settings_page/settings_page.css">
     <link rel="stylesheet" href="date_time_page.css">
     <paper-shadow layout vertical cross-fade>
-      <cr-settings-page-header page="{{}}"></cr-settings-page-header>
       <div horizontal layout center>
         <core-label horizontal layout center>
           <span class="time-zone-label">Time zone:</span>
diff --git a/chrome/browser/resources/settings/date_time_page/date_time_page.js b/chrome/browser/resources/settings/date_time_page/date_time_page.js
index 210c753b..f91d327 100644
--- a/chrome/browser/resources/settings/date_time_page/date_time_page.js
+++ b/chrome/browser/resources/settings/date_time_page/date_time_page.js
@@ -30,6 +30,15 @@
     prefs: null,
 
     /**
+     * Whether the page is a subpage.
+     *
+     * @attribute subpage
+     * @type boolean
+     * @default false
+     */
+    subpage: false,
+
+    /**
      * ID of the page.
      *
      * @attribute PAGE_ID
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html
index 72b707a..b9947e0 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.html
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -11,7 +11,6 @@
     <link rel="stylesheet" href="chrome://md-settings/settings_page/settings_page.css">
     <link rel="stylesheet" href="downloads_page.css">
     <paper-shadow layout vertical cross-fade>
-      <cr-settings-page-header page="{{}}"></cr-settings-page-header>
       <div horizontal layout center>
         <core-label horizontal layout center>
           <span class="location-label" i18n-content="downloadsLocationLabel"></span>
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.js b/chrome/browser/resources/settings/downloads_page/downloads_page.js
index 59d1303..f2581919 100644
--- a/chrome/browser/resources/settings/downloads_page/downloads_page.js
+++ b/chrome/browser/resources/settings/downloads_page/downloads_page.js
@@ -30,6 +30,15 @@
     prefs: null,
 
     /**
+     * Whether the page is a subpage.
+     *
+     * @attribute subpage
+     * @type boolean
+     * @default false
+     */
+    subpage: false,
+
+    /**
      * ID of the page.
      *
      * @attribute PAGE_ID
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.html b/chrome/browser/resources/settings/internet_page/internet_page.html
new file mode 100644
index 0000000..0b1b285
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -0,0 +1,12 @@
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="network_summary.html">
+
+<polymer-element name="cr-settings-internet-page">
+  <template>
+    <link rel="stylesheet" href="chrome://md-settings/settings_page/settings_page.css">
+    <paper-shadow layout vertical cross-fade>
+      <cr-network-summary></cr-network-summary>
+    </paper-shadow>
+  </template>
+  <script src="internet_page.js"></script>
+</polymer-element>
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
new file mode 100644
index 0000000..7b16bb39
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -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.
+
+/**
+ * @fileoverview
+ * 'cr-settings-internet-page' is the settings page containing internet
+ * settings.
+ *
+ * Example:
+ *
+ *    <core-animated-pages>
+ *      <cr-settings-internet-page prefs="{{prefs}}">
+ *      </cr-settings-internet-page>
+ *      ... other pages ...
+ *    </core-animated-pages>
+ *
+ * @group Chrome Settings Elements
+ * @element cr-settings-internet-page
+ */
+Polymer('cr-settings-internet-page', {
+  publish: {
+    /**
+     * ID of the page.
+     *
+     * @attribute PAGE_ID
+     * @const string
+     */
+    PAGE_ID: 'internet',
+
+    /**
+     * Whether the page is a subpage.
+     *
+     * @attribute subpage
+     * @type boolean
+     * @default false
+     */
+    subpage: false,
+
+    /**
+     * Title for the page header and navigation menu.
+     *
+     * @attribute pageTitle
+     * @type string
+     */
+    pageTitle: loadTimeData.getString('internetPageTitle'),
+
+    /**
+     * Name of the 'core-icon' to show. TODO(stevenjb): Update this with the
+     * icon for the active internet connection.
+     *
+     * @attribute icon
+     * @type string
+     * @default 'settings-ethernet'
+     */
+    icon: 'settings-ethernet',
+  },
+});
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.css b/chrome/browser/resources/settings/internet_page/network_summary.css
new file mode 100644
index 0000000..b956a77
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/network_summary.css
@@ -0,0 +1,7 @@
+/* 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. */
+
+cr-network-list-item {
+  margin-bottom: 10px;
+}
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.html b/chrome/browser/resources/settings/internet_page/network_summary.html
new file mode 100644
index 0000000..f4de128
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/network_summary.html
@@ -0,0 +1,27 @@
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_network_list_item/cr_network_list_item.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_onc/cr_onc_data.html">
+
+<polymer-element name="cr-network-summary">
+  <template>
+    <link rel="stylesheet" href="network_summary.css">
+    <div id="summary" vertical layout>
+      <cr-network-list-item networkState="{{networkStates.Ethernet}}"
+          hidden?="{{!networkStates.Ethernet}}">
+      </cr-network-list-item>
+      <cr-network-list-item networkState="{{networkStates.WiFi}}"
+          hidden?="{{!networkStates.WiFi}}">
+      </cr-network-list-item>
+      <cr-network-list-item networkState="{{networkStates.Cellular}}"
+          hidden?="{{!networkStates.Cellular}}">
+      </cr-network-list-item>
+      <cr-network-list-item networkState="{{networkStates.WiMAX}}"
+          hidden?="{{!networkStates.WiMAX}}">
+      </cr-network-list-item>
+      <cr-network-list-item networkState="{{networkStates.VPN}}"
+          hidden?="{{!networkStates.VPN}}">
+      </cr-network-list-item>
+    </div>
+  </template>
+  <script src="network_summary.js"></script>
+</polymer-element>
diff --git a/chrome/browser/resources/settings/internet_page/network_summary.js b/chrome/browser/resources/settings/internet_page/network_summary.js
new file mode 100644
index 0000000..34a4b9a
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/network_summary.js
@@ -0,0 +1,161 @@
+// 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.
+
+/**
+ * @fileoverview Polymer element for displaying a summary of network states
+ * by type: Ethernet, WiFi, Cellular, WiMAX, and VPN.
+ */
+Polymer('cr-network-summary', {
+  publish: {
+    /**
+     * Network state data for each network type.
+     *
+     * @attribute networkStates
+     * @type {{
+     *   Ethernet: (CrOncDataElement|undefined),
+     *   WiFi: (CrOncDataElement|undefined),
+     *   Cellular: (CrOncDataElement|undefined),
+     *   WiMAX: (CrOncDataElement|undefined),
+     *   VPN: (CrOncDataElement|undefined)
+     * }}
+     * @default {}
+     */
+    networkStates: null,
+  },
+
+  /**
+   * Listener function for chrome.networkingPrivate.onNetworkListChanged event.
+   * @type {function(!Array<string>)}
+   * @private
+   */
+  listChangedListener_: null,
+
+  /**
+   * Listener function for chrome.networkingPrivate.onNetworksChanged event.
+   * @type {function(!Array<string>)}
+   * @private
+   */
+  networksChangedListener_: null,
+
+  /**
+   * Dictionary of GUIDs identifying primary (active) networks for each type.
+   * @type {Object}
+   * @private
+   */
+  networkIds_: {},
+
+  /** @override */
+  created: function() {
+    this.networkStates = {};
+  },
+
+  /** @override */
+  attached: function() {
+    this.getNetworks_();
+
+    this.listChangedListener_ = this.onNetworkListChangedEvent_.bind(this);
+    chrome.networkingPrivate.onNetworkListChanged.addListener(
+        this.listChangedListener_);
+
+    this.networksChangedListener_ = this.onNetworksChangedEvent_.bind(this);
+    chrome.networkingPrivate.onNetworksChanged.addListener(
+        this.networksChangedListener_);
+  },
+
+  /** @override */
+  detached: function() {
+    chrome.networkingPrivate.onNetworkListChanged.removeListener(
+        this.listChangedListener_);
+
+    chrome.networkingPrivate.onNetworksChanged.removeListener(
+        this.networksChangedListener_);
+  },
+
+  /**
+   * @private
+   */
+  onNetworkListChangedEvent_: function() {
+    this.getNetworks_();
+  },
+
+  /**
+   * @param {!Array<string>} networkIds The list of changed network GUIDs.
+   * @private
+   */
+  onNetworksChangedEvent_: function(networkIds) {
+    networkIds.forEach(function(id) {
+      if (id in this.networkIds_) {
+        chrome.networkingPrivate.getState(id,
+                                          this.getStateCallback_.bind(this));
+      }
+    }, this);
+  },
+
+  /** @private */
+  getNetworks_: function() {
+    var filter = {
+      networkType: 'All',
+      visible: true,
+      configured: false
+    };
+    chrome.networkingPrivate.getNetworks(filter,
+                                         this.getNetworksCallback_.bind(this));
+  },
+
+  /**
+   * @param {!Array<!chrome.networkingPrivate.NetworkStateProperties>} states
+   *     The state properties for all networks.
+   * @private
+   */
+  getNetworksCallback_: function(states) {
+    // Clear all active networks.
+    this.networkIds_ = {};
+
+    // Get the first (active) state for each type.
+    var foundTypes = {};
+    states.forEach(function(state) {
+      var type = state.Type;
+      if (!foundTypes[type]) {
+        foundTypes[type] = true;
+        this.updateNetworkState_(type, state);
+      }
+    }, this);
+
+    // Set any types not found to null. TODO(stevenjb): Support types that are
+    // disabled but available with no active network.
+    var types = ['Ethernet', 'WiFi', 'Cellular', 'WiMAX', 'VPN'];
+    types.forEach(function(type) {
+      if (!foundTypes[type])
+        this.updateNetworkState_(type, null);
+    }, this);
+  },
+
+  /**
+   * @param {!chrome.networkingPrivate.NetworkStateProperties} state The state
+   *     properties for the network.
+   * @private
+   */
+  getStateCallback_: function(state) {
+    var id = state.GUID;
+    if (!this.networkIds_[id])
+      return;
+    this.updateNetworkState_(state.Type, state);
+  },
+
+  /**
+   * Creates a CrOncDataElement from the network state (if not null) for 'type'.
+   * Sets 'networkStates[type]' which will update the cr-network-list-item
+   * associated with 'type'.
+   * @param {string} type The network type.
+   * @param {chrome.networkingPrivate.NetworkStateProperties} state The state
+   *     properties for the network to associate with |type|. May be null if
+   *     there are no networks matching |type|.
+   * @private
+   */
+  updateNetworkState_: function(type, state) {
+    this.networkStates[type] = state ? CrOncDataElement.create(state) : null;
+    if (state)
+      this.networkIds_[state.GUID] = true;
+  },
+});
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.css b/chrome/browser/resources/settings/settings_main/settings_main.css
index 59f7f0b..73a8a32 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.css
+++ b/chrome/browser/resources/settings/settings_main/settings_main.css
@@ -8,12 +8,19 @@
   display: block;
 }
 
-#outer {
+#mainContainer {
   box-sizing: border-box;
   height: 100%;
   overflow: auto;
 }
 
+#mainContainer > paper-shadow {
+  -webkit-padding-start: 80px;
+  background-color: white;
+  padding-bottom: 40px;
+  padding-top: 40px;
+}
+
 #pageContainer {
   -webkit-margin-end: 20px;
   -webkit-margin-start: 20px;
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index cc96389d..a357f1a8 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -5,22 +5,35 @@
 <link rel="import" href="chrome://md-settings/a11y_page/a11y_page.html">
 <link rel="import" href="chrome://md-settings/date_time_page/date_time_page.html">
 <link rel="import" href="chrome://md-settings/downloads_page/downloads_page.html">
+<link rel="import" href="chrome://md-settings/internet_page/internet_page.html">
+<link rel="import" href="chrome://md-settings/routing/more_routing/more_routing.html">
 
+<!-- Drivers manage how the URL is read, and how to navigate to URLs. "path"
+     based driver means URLs like /a11y are generated. For more details, see
+     http://goo.gl/maQU6V -->
+<more-routing-config driver="path"></more-routing-config>
 <polymer-element name="cr-settings-main">
   <template>
     <link rel="stylesheet" href="settings_main.css">
-    <div id="outer">
+    <div id="mainContainer">
       <content select="core-icon-button"></content>
-      <core-animated-pages id="pageContainer" selected="{{selectedPageId}}"
-          valueattr="PAGE_ID" transitions="cross-fade">
-        <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page>
+      <more-route-selector>
+        <core-animated-pages id="pageContainer" selected="{{selectedPageId}}"
+            valueattr="PAGE_ID" transitions="cross-fade">
 <if expr="chromeos">
-        <cr-settings-date-time-page prefs="{{prefs}}">
-        </cr-settings-date-time-page>
+          <cr-settings-internet-page route="/internet">
+          </cr-settings-internet-page>
 </if>
-        <cr-settings-downloads-page prefs="{{prefs}}">
-        </cr-settings-downloads-page>
-      </core-animated-pages>
+          <cr-settings-a11y-page prefs="{{prefs}}" route="/a11y">
+          </cr-settings-a11y-page>
+<if expr="chromeos">
+          <cr-settings-date-time-page prefs="{{prefs}}" route="/datetime">
+          </cr-settings-date-time-page>
+</if>
+          <cr-settings-downloads-page prefs="{{prefs}}" route="/downloads">
+          </cr-settings-downloads-page>
+        </core-animated-pages>
+      </more-route-selector>
     </div>
   </template>
   <script src="settings_main.js"></script>
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.js b/chrome/browser/resources/settings/settings_main/settings_main.js
index 582cfb4..42fc54a7 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.js
+++ b/chrome/browser/resources/settings/settings_main/settings_main.js
@@ -58,8 +58,7 @@
                      /** @type {MutationObserverInit} */ {
                        childList: true,
                      });
-    this.pages = this.$.pageContainer.items;
-    this.ensureSelection_();
+    this.pageContainerUpdated_();
   },
 
   /**
@@ -81,7 +80,9 @@
    * @private
    */
   pageContainerUpdated_: function() {
-    this.pages = this.$.pageContainer.items;
+    this.pages = this.$.pageContainer.items.filter(function(item) {
+      return !item.subpage;
+    });
     this.ensureSelection_();
   },
 });
diff --git a/chrome/browser/resources/settings/settings_page/settings_page.css b/chrome/browser/resources/settings/settings_page/settings_page.css
index 9a0d23b2..c3a0ea0 100644
--- a/chrome/browser/resources/settings/settings_page/settings_page.css
+++ b/chrome/browser/resources/settings/settings_page/settings_page.css
@@ -24,4 +24,3 @@
   margin-bottom: 10px;
   margin-top: 10px;
 }
-
diff --git a/chrome/browser/resources/settings/settings_page/settings_page_header.js b/chrome/browser/resources/settings/settings_page/settings_page_header.js
index bd485f74..a36911a 100644
--- a/chrome/browser/resources/settings/settings_page/settings_page_header.js
+++ b/chrome/browser/resources/settings/settings_page/settings_page_header.js
@@ -24,5 +24,42 @@
      * @default null
      */
     page: null,
+
+    /**
+     * The current stack of pages being viewed. I.e., may contain subpages.
+     *
+     * @attribute pageStack
+     * @type Array
+     * @default null
+     */
+    pageStack: null,
+
+    /**
+     * The selector determining which page is currently selected.
+     *
+     * @attribute selector
+     * @type HTMLElement
+     * @default null
+     */
+    selector: null,
   },
+
+  ready: function() {
+    this.pageStack = [];
+  },
+
+  selectorChanged: function() {
+    this.selector.addEventListener('core-select', function() {
+      // core-select gets fired once for deselection and once for selection;
+      // ignore the deselection case.
+      if (!this.selector.selectedItem)
+        return;
+
+      if (this.selector.selectedItem.subpage) {
+        this.pageStack.push(this.selector.selectedItem);
+      } else {
+        this.pageStack = [this.selector.selectedItem];
+      }
+    }.bind(this));
+  }
 });
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index ccee110..c538c67 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -85,27 +85,27 @@
       <structure name="IDR_SETTINGS_DATE_TIME_PAGE_CSS"
                  file="date_time_page/date_time_page.css"
                  type="chrome_html" />
-      <structure name="IDR_SETTINGS_DATE_TIME_PAGE_JS"
-                 file="date_time_page/date_time_page.js"
-                 type="chrome_html" />
       <structure name="IDR_SETTINGS_DATE_TIME_PAGE_HTML"
                  file="date_time_page/date_time_page.html"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_DATE_TIME_PAGE_JS"
+                 file="date_time_page/date_time_page.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_CSS"
                  file="downloads_page/downloads_page.css"
                  type="chrome_html" />
-      <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_JS"
-                 file="downloads_page/downloads_page.js"
-                 type="chrome_html" />
       <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_HTML"
                  file="downloads_page/downloads_page.html"
                  type="chrome_html" />
-      <structure name="IDR_SETTINGS_PREFS_JS"
-                 file="prefs/prefs.js"
+      <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_JS"
+                 file="downloads_page/downloads_page.js"
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_PREFS_HTML"
                  file="prefs/prefs.html"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_PREFS_JS"
+                 file="prefs/prefs.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_PREFS_TYPES_JS"
                  file="prefs/prefs_types.js"
                  type="chrome_html" />
@@ -208,6 +208,23 @@
       <structure name="IDR_SETTINGS_SETTINGS_JS"
                  file="settings.js"
                  type="chrome_html" />
+      <if expr="chromeos">
+        <structure name="IDR_SETTINGS_INTERNET_PAGE_HTML"
+                   file="internet_page/internet_page.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_INTERNET_PAGE_JS"
+                   file="internet_page/internet_page.js"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_NETWORK_SUMMARY_CSS"
+                   file="internet_page/network_summary.css"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_NETWORK_SUMMARY_HTML"
+                   file="internet_page/network_summary.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_NETWORK_SUMMARY_JS"
+                   file="internet_page/network_summary.js"
+                   type="chrome_html" />
+      </if>
     </structures>
   </release>
 </grit>
diff --git a/chrome/browser/safe_browsing/database_manager.cc b/chrome/browser/safe_browsing/database_manager.cc
index 0327d9f..afcd0f69 100644
--- a/chrome/browser/safe_browsing/database_manager.cc
+++ b/chrome/browser/safe_browsing/database_manager.cc
@@ -12,7 +12,6 @@
 #include "base/command_line.h"
 #include "base/debug/leak_tracker.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
@@ -652,9 +651,6 @@
 }
 
 void SafeBrowsingDatabaseManager::StartOnIOThread() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("455469 StartOnIOThread"));
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (enabled_)
     return;
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 53e26ba..d657e661 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -393,6 +393,10 @@
 
 void SafeBrowsingService::InitURLRequestContextOnIOThread(
     net::URLRequestContextGetter* system_url_request_context_getter) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::InitURLRequestContextOnIOThread 1"));
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!url_request_context_.get());
 
@@ -404,6 +408,10 @@
               NULL,
               NULL)));
 
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::InitURLRequestContextOnIOThread 2"));
   url_request_context_.reset(new net::URLRequestContext);
   // |system_url_request_context_getter| may be NULL during tests.
   if (system_url_request_context_getter) {
@@ -464,6 +472,10 @@
 
 void SafeBrowsingService::StartOnIOThread(
     net::URLRequestContextGetter* url_request_context_getter) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::StartOnIOThread 1"));
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   if (enabled_)
     return;
@@ -472,15 +484,27 @@
   SafeBrowsingProtocolConfig config = GetProtocolConfig();
 
 #if defined(FULL_SAFE_BROWSING)
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::StartOnIOThread 2"));
   DCHECK(database_manager_.get());
   database_manager_->StartOnIOThread();
 
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile3(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::StartOnIOThread 3"));
   DCHECK(!protocol_manager_);
   protocol_manager_ = SafeBrowsingProtocolManager::Create(
       database_manager_.get(), url_request_context_getter, config);
   protocol_manager_->Initialize();
 #endif
 
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile4(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingService::StartOnIOThread 4"));
   DCHECK(!ping_manager_);
   ping_manager_ = SafeBrowsingPingManager::Create(
       url_request_context_getter, config);
diff --git a/chrome/browser/sessions/session_restore_delegate.cc b/chrome/browser/sessions/session_restore_delegate.cc
index 9a40060..d409d38 100644
--- a/chrome/browser/sessions/session_restore_delegate.cc
+++ b/chrome/browser/sessions/session_restore_delegate.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/field_trial.h"
 #include "chrome/browser/sessions/session_restore_stats_collector.h"
 #include "chrome/browser/sessions/tab_loader.h"
+#include "components/favicon/content/content_favicon_driver.h"
 
 // static
 void SessionRestoreDelegate::RestoreTabs(
@@ -20,6 +21,16 @@
   if (!trial || trial->group_name() == "Restore") {
     TabLoader::RestoreTabs(tabs, restore_started);
     active_only = false;
+  } else {
+    // If we are not loading inactive tabs, restore their favicons (title has
+    // already been set by now).
+    for (auto& tab : tabs) {
+      if (!tab.is_active) {
+        favicon::ContentFaviconDriver* favicon_driver =
+            favicon::ContentFaviconDriver::FromWebContents(tab.contents);
+        favicon_driver->FetchFavicon(favicon_driver->GetActiveURL());
+      }
+    }
   }
   SessionRestoreStatsCollector::TrackTabs(tabs, restore_started, active_only);
 }
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 680c9e9..debf8eca4 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -23,6 +23,7 @@
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/signin/core/common/signin_pref_names.h"
 #include "components/signin/core/common/signin_switches.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/gurl.h"
 
@@ -38,12 +39,6 @@
 #include "chrome/browser/first_run/first_run.h"
 #endif
 
-namespace {
-
-const char kGoogleAccountsUrl[] = "https://accounts.google.com";
-
-}  // namespace
-
 ChromeSigninClient::ChromeSigninClient(
     Profile* profile, SigninErrorController* signin_error_controller)
     : profile_(profile),
@@ -65,9 +60,9 @@
 // static
 bool ChromeSigninClient::SettingsAllowSigninCookies(
     CookieSettings* cookie_settings) {
+  GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
   return cookie_settings &&
-         cookie_settings->IsSettingCookieAllowed(GURL(kGoogleAccountsUrl),
-                                                 GURL(kGoogleAccountsUrl));
+         cookie_settings->IsSettingCookieAllowed(gaia_url, gaia_url);
 }
 
 PrefService* ChromeSigninClient::GetPrefs() { return profile_->GetPrefs(); }
diff --git a/chrome/browser/sync/glue/password_data_type_controller.cc b/chrome/browser/sync/glue/password_data_type_controller.cc
index 354d8f9..71b589a 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.cc
+++ b/chrome/browser/sync/glue/password_data_type_controller.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
 #include "chrome/browser/sync/profile_sync_components_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -50,9 +52,29 @@
 bool PasswordDataTypeController::StartModels() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK_EQ(MODEL_STARTING, state());
+
+  ProfileSyncService* profile_sync_service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
+  DCHECK(profile_sync_service);
+  profile_sync_service->AddObserver(this);
+
+  OnStateChanged();
+
   password_store_ = PasswordStoreFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
   return !!password_store_.get();
 }
 
+void PasswordDataTypeController::StopModels() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  ProfileSyncService* profile_sync_service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
+  DCHECK(profile_sync_service);
+  profile_sync_service->RemoveObserver(this);
+}
+
+void PasswordDataTypeController::OnStateChanged() {
+  PasswordStoreFactory::OnPasswordsSyncedStatePotentiallyChanged(profile_);
+}
+
 }  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/password_data_type_controller.h b/chrome/browser/sync/glue/password_data_type_controller.h
index ae59542..a2bceb4b 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.h
+++ b/chrome/browser/sync/glue/password_data_type_controller.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "components/sync_driver/non_ui_data_type_controller.h"
+#include "components/sync_driver/sync_service_observer.h"
 
 class Profile;
 class ProfileSyncComponentsFactory;
@@ -20,7 +21,8 @@
 namespace browser_sync {
 
 // A class that manages the startup and shutdown of password sync.
-class PasswordDataTypeController : public sync_driver::NonUIDataTypeController {
+class PasswordDataTypeController : public sync_driver::NonUIDataTypeController,
+                                   public sync_driver::SyncServiceObserver {
  public:
   PasswordDataTypeController(
       ProfileSyncComponentsFactory* profile_sync_factory,
@@ -37,6 +39,10 @@
   bool PostTaskOnBackendThread(const tracked_objects::Location& from_here,
                                const base::Closure& task) override;
   bool StartModels() override;
+  void StopModels() override;
+
+  // sync_driver::SyncServiceObserver:
+  void OnStateChanged() override;
 
  private:
   Profile* const profile_;
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.cc b/chrome/browser/sync/glue/typed_url_model_associator.cc
index 373fc7c..e758b385 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.cc
+++ b/chrome/browser/sync/glue/typed_url_model_associator.cc
@@ -162,7 +162,8 @@
     syncer::SyncMergeResult* local_merge_result,
     syncer::SyncMergeResult* syncer_merge_result) {
   ClearErrorStats();
-  syncer::SyncError error = DoAssociateModels();
+  syncer::SyncError error =
+      DoAssociateModels(local_merge_result, syncer_merge_result);
   UMA_HISTOGRAM_PERCENTAGE("Sync.TypedUrlModelAssociationErrors",
                            GetErrorPercentage());
   ClearErrorStats();
@@ -178,7 +179,9 @@
   return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0;
 }
 
-syncer::SyncError TypedUrlModelAssociator::DoAssociateModels() {
+syncer::SyncError TypedUrlModelAssociator::DoAssociateModels(
+    syncer::SyncMergeResult* local_merge_result,
+    syncer::SyncMergeResult* syncer_merge_result) {
   DVLOG(1) << "Associating TypedUrl Models";
   DCHECK(expected_loop_ == base::MessageLoop::current());
 
@@ -207,6 +210,7 @@
           "Could not get the typed_url entries.",
           model_type());
     }
+    local_merge_result->set_num_items_before_association(typed_urls.size());
 
     // Get all the visits.
     std::map<history::URLID, history::VisitVector> visit_vectors;
@@ -234,6 +238,8 @@
           "might be running against an out-of-date server.",
           model_type());
     }
+    syncer_merge_result->set_num_items_before_association(
+        typed_url_root.GetTotalNodeCount());
 
     std::set<std::string> current_urls;
     for (history::URLRows::iterator ix = typed_urls.begin();
@@ -288,6 +294,8 @@
           DCHECK_EQ(new_url.last_visit().ToInternalValue(),
                     visits.back().visit_time.ToInternalValue());
           WriteToSyncNode(new_url, visits, &write_node);
+          syncer_merge_result->set_num_items_modified(
+              syncer_merge_result->num_items_modified() + 1);
         }
         if (difference & DIFF_LOCAL_ROW_CHANGED) {
           DCHECK_EQ(ix->id(), new_url.id());
@@ -313,6 +321,8 @@
 
         node.SetTitle(tag);
         WriteToSyncNode(*ix, visits, &node);
+        syncer_merge_result->set_num_items_added(
+            syncer_merge_result->num_items_added() + 1);
       }
 
       current_urls.insert(tag);
@@ -373,6 +383,8 @@
                          NULL,
                          &updated_urls,
                          &new_urls);
+        local_merge_result->set_num_items_added(
+            local_merge_result->num_items_added() + 1);
       }
     }
 
@@ -392,6 +404,8 @@
         sync_node.Tombstone();
       }
     }
+    syncer_merge_result->set_num_items_after_association(
+        typed_url_root.GetTotalNodeCount());
   }
 
   // Since we're on the history thread, we don't have to worry about updating
@@ -400,6 +414,10 @@
   // to worry about the sync model getting out of sync, because changes are
   // propagated to the ChangeProcessor on this thread.
   WriteToHistoryBackend(&new_urls, &updated_urls, &new_visits, NULL);
+  local_merge_result->set_num_items_modified(updated_urls.size());
+  local_merge_result->set_num_items_after_association(
+      local_merge_result->num_items_before_association() +
+      local_merge_result->num_items_added());
   return syncer::SyncError();
 }
 
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.h b/chrome/browser/sync/glue/typed_url_model_associator.h
index 44e9ea7b..8ae5319 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.h
+++ b/chrome/browser/sync/glue/typed_url_model_associator.h
@@ -174,7 +174,9 @@
  private:
 
   // Helper routine that actually does the work of associating models.
-  syncer::SyncError DoAssociateModels();
+  syncer::SyncError DoAssociateModels(
+      syncer::SyncMergeResult* local_merge_result,
+      syncer::SyncMergeResult* syncer_merge_result);
 
   // Helper function that determines if we should ignore a URL for the purposes
   // of sync, based on the visits the URL had.
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
index cff2ac5..2ee3dc2 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
@@ -2448,10 +2448,11 @@
   HidePopup();
 }
 
-void AutofillDialogControllerImpl::RemoveSuggestion(
+bool AutofillDialogControllerImpl::RemoveSuggestion(
     const base::string16& value,
     int identifier) {
   // TODO(estade): implement.
+  return false;
 }
 
 void AutofillDialogControllerImpl::ClearPreviewedForm() {
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
index 80e5a5fd..27db8f7 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
@@ -169,7 +169,7 @@
                            int identifier) override;
   void DidAcceptSuggestion(const base::string16& value,
                            int identifier) override;
-  void RemoveSuggestion(const base::string16& value, int identifier) override;
+  bool RemoveSuggestion(const base::string16& value, int identifier) override;
   void ClearPreviewedForm() override;
 
   // content::NotificationObserver implementation.
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller.h b/chrome/browser/ui/autofill/autofill_popup_controller.h
index a5a723ca..c1631a0 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller.h
@@ -34,9 +34,6 @@
   // resource isn't recognized.
   virtual int GetIconResourceID(const base::string16& resource_name) const = 0;
 
-  // Returns true if the given index refers to an element that can be deleted.
-  virtual bool CanDelete(size_t index) const = 0;
-
   // Returns true if the given index refers to an element that is a warning
   // rather than an Autofill suggestion.
   virtual bool IsWarning(size_t index) const = 0;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 2a659829..5a80f67 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -356,14 +356,6 @@
   return -1;
 }
 
-bool AutofillPopupControllerImpl::CanDelete(size_t index) const {
-  // TODO(isherman): Native AddressBook suggestions on Mac and Android should
-  // not be considered to be deleteable.
-  int id = suggestions_[index].frontend_id;
-  return id > 0 || id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY ||
-         id == POPUP_ITEM_ID_PASSWORD_ENTRY;
-}
-
 bool AutofillPopupControllerImpl::IsWarning(size_t index) const {
   return suggestions_[index].frontend_id == POPUP_ITEM_ID_WARNING_MESSAGE;
 }
@@ -508,11 +500,10 @@
   DCHECK_GE(selected_line_, 0);
   DCHECK_LT(selected_line_, static_cast<int>(GetLineCount()));
 
-  if (!CanDelete(selected_line_))
+  if (!delegate_->RemoveSuggestion(suggestions_[selected_line_].value,
+                                   suggestions_[selected_line_].frontend_id)) {
     return false;
-
-  delegate_->RemoveSuggestion(suggestions_[selected_line_].value,
-                              suggestions_[selected_line_].frontend_id);
+  }
 
   // Remove the deleted element.
   suggestions_.erase(suggestions_.begin() + selected_line_);
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index 8e84b6cd..1f26da9 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -76,7 +76,6 @@
   void SelectionCleared() override;
   void AcceptSuggestion(size_t index) override;
   int GetIconResourceID(const base::string16& resource_name) const override;
-  bool CanDelete(size_t index) const override;
   bool IsWarning(size_t index) const override;
   gfx::Rect GetRowBounds(size_t index) override;
   void SetPopupBounds(const gfx::Rect& bounds) override;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index 71e262aa..122bcd90 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -47,7 +47,9 @@
 
   void DidSelectSuggestion(const base::string16& value,
                            int identifier) override {}
-  void RemoveSuggestion(const base::string16& value, int identifier) override {}
+  bool RemoveSuggestion(const base::string16& value, int identifier) override {
+    return true;
+  }
   void ClearPreviewedForm() override {}
   base::WeakPtr<AutofillExternalDelegate> GetWeakPtr() {
     return AutofillExternalDelegate::GetWeakPtr();
@@ -248,12 +250,6 @@
   // No line is selected so the removal should fail.
   EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
 
-  // Try to remove the last entry and ensure it fails (it is an option).
-  autofill_popup_controller_->SetSelectedLine(
-      autofill_popup_controller_->GetLineCount() - 1);
-  EXPECT_FALSE(autofill_popup_controller_->RemoveSelectedLine());
-  EXPECT_LE(0, autofill_popup_controller_->selected_line());
-
   // Remove the first entry. The popup should be redrawn since its size has
   // changed.
   EXPECT_CALL(*autofill_popup_controller_, UpdateBoundsAndRedrawPopup());
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
index 19f9ac8a..dc640a1 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -171,9 +171,6 @@
   bool IsPopupRunning() const override;
   void OnOverflowedActionWantsToRunChanged(bool overflowed_action_wants_to_run)
       override;
-  void ShowExtensionMessageBubble(
-      scoped_ptr<extensions::ExtensionMessageBubbleController> controller)
-          override;
 
   // The owning BrowserActionsController; weak.
   BrowserActionsController* controller_;
@@ -251,11 +248,6 @@
       setOverflowedToolbarActionWantsToRun:overflowed_action_wants_to_run];
 }
 
-void ToolbarActionsBarBridge::ShowExtensionMessageBubble(
-    scoped_ptr<extensions::ExtensionMessageBubbleController> controller) {
-  NOTREACHED();  // Not yet implemented on Mac.
-}
-
 }  // namespace
 
 @implementation BrowserActionsController
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
index 617cc56..df7b00d3 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/omnibox/suggestion_answer.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/font.h"
 
@@ -180,7 +181,23 @@
       match_.contents, ContentTextColor(), match_.contents_class);
   [self setAttributedTitle:contents];
 
-  if (match_.description.empty()) {
+  if (match_.answer) {
+    base::string16 answerString;
+    DCHECK(!match_.answer->second_line().text_fields().empty());
+    for (const SuggestionAnswer::TextField& textField :
+         match_.answer->second_line().text_fields())
+      answerString += textField.text();
+    const base::char16 space(' ');
+    const SuggestionAnswer::TextField* textField =
+        match_.answer->second_line().additional_text();
+    if (textField)
+      answerString += space + textField->text();
+    textField = match_.answer->second_line().status_text();
+    if (textField)
+      answerString += space + textField->text();
+    description_.reset([CreateClassifiedAttributedString(
+        answerString, DimTextColor(), match_.description_class) retain]);
+  } else if (match_.description.empty()) {
     description_.reset();
   } else {
     description_.reset([CreateClassifiedAttributedString(
diff --git a/chrome/browser/ui/extensions/OWNERS b/chrome/browser/ui/extensions/OWNERS
index e7cdf1a..98867c4 100644
--- a/chrome/browser/ui/extensions/OWNERS
+++ b/chrome/browser/ui/extensions/OWNERS
@@ -1,9 +1,6 @@
 benwells@chromium.org
 miket@chromium.org
 
+# Extension/Toolbar Actions Files
 per-file extension_action*=finnur@chromium.org
 per-file extension_action*=rdevlin.cronin@chromium.org
-per-file extension_installed_bubble*=finnur@chromium.org
-per-file extension_installed_bubble*=rdevlin.cronin@chromium.org
-per-file extension_message_bubble*=finnur@chromium.org
-per-file extension_message_bubble*=rdevlin.cronin@chromium.org
diff --git a/chrome/browser/ui/extensions/extension_message_bubble_factory.cc b/chrome/browser/ui/extensions/extension_message_bubble_factory.cc
deleted file mode 100644
index 8737602..0000000
--- a/chrome/browser/ui/extensions/extension_message_bubble_factory.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 "chrome/browser/ui/extensions/extension_message_bubble_factory.h"
-
-#include "base/lazy_instance.h"
-#include "chrome/browser/extensions/dev_mode_bubble_controller.h"
-#include "chrome/browser/extensions/extension_message_bubble_controller.h"
-#include "chrome/browser/extensions/proxy_overridden_bubble_controller.h"
-#include "chrome/browser/extensions/settings_api_bubble_controller.h"
-#include "chrome/browser/extensions/settings_api_helpers.h"
-#include "chrome/browser/extensions/suspicious_extension_bubble_controller.h"
-#include "chrome/browser/profiles/profile.h"
-
-namespace {
-
-// A map of all profiles evaluated, so we can tell if it's the initial check.
-// TODO(devlin): It would be nice to coalesce all the "profiles evaluated" maps
-// that are in the different bubble controllers.
-base::LazyInstance<std::set<Profile*> > g_profiles_evaluated =
-    LAZY_INSTANCE_INITIALIZER;
-
-}
-
-ExtensionMessageBubbleFactory::ExtensionMessageBubbleFactory(Profile* profile)
-    : profile_(profile) {
-}
-
-ExtensionMessageBubbleFactory::~ExtensionMessageBubbleFactory() {
-}
-
-scoped_ptr<extensions::ExtensionMessageBubbleController>
-ExtensionMessageBubbleFactory::GetController() {
-// Currently only on windows.
-#if !defined(OS_WIN)
-  return scoped_ptr<extensions::ExtensionMessageBubbleController>();
-#endif
-
-  Profile* original_profile = profile_->GetOriginalProfile();
-  std::set<Profile*>& profiles_evaluated = g_profiles_evaluated.Get();
-  bool is_initial_check = profiles_evaluated.count(original_profile) == 0;
-  profiles_evaluated.insert(original_profile);
-
-  // The list of suspicious extensions takes priority over the dev mode bubble
-  // and the settings API bubble, since that needs to be shown as soon as we
-  // disable something. The settings API bubble is shown on first startup after
-  // an extension has changed the startup pages and it is acceptable if that
-  // waits until the next startup because of the suspicious extension bubble.
-  // The dev mode bubble is not time sensitive like the other two so we'll catch
-  // the dev mode extensions on the next startup/next window that opens. That
-  // way, we're not too spammy with the bubbles.
-  {
-    scoped_ptr<extensions::SuspiciousExtensionBubbleController> controller(
-        new extensions::SuspiciousExtensionBubbleController(profile_));
-    if (controller->ShouldShow())
-      return controller.Pass();
-  }
-
-  {
-    // No use showing this if it's not the startup of the profile.
-    if (is_initial_check) {
-      scoped_ptr<extensions::SettingsApiBubbleController> controller(
-          new extensions::SettingsApiBubbleController(
-              profile_, extensions::BUBBLE_TYPE_STARTUP_PAGES));
-      if (controller->ShouldShow())
-        return controller.Pass();
-    }
-  }
-
-  {
-    // TODO(devlin): Move the "GetExtensionOverridingProxy" part into the
-    // proxy bubble controller.
-    const extensions::Extension* extension =
-        extensions::GetExtensionOverridingProxy(profile_);
-    if (extension) {
-      scoped_ptr<extensions::ProxyOverriddenBubbleController> controller(
-         new extensions::ProxyOverriddenBubbleController(profile_));
-      if (controller->ShouldShow(extension->id()))
-        return controller.Pass();
-    }
-  }
-
-  {
-    scoped_ptr<extensions::DevModeBubbleController> controller(
-        new extensions::DevModeBubbleController(profile_));
-    if (controller->ShouldShow())
-      return controller.Pass();
-  }
-
-  return scoped_ptr<extensions::ExtensionMessageBubbleController>();
-}
diff --git a/chrome/browser/ui/extensions/extension_message_bubble_factory.h b/chrome/browser/ui/extensions/extension_message_bubble_factory.h
deleted file mode 100644
index 703cd30..0000000
--- a/chrome/browser/ui/extensions/extension_message_bubble_factory.h
+++ /dev/null
@@ -1,34 +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 CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_FACTORY_H_
-#define CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-
-class Profile;
-
-namespace extensions {
-class ExtensionMessageBubbleController;
-}  // namespace extensions
-
-// Create and show ExtensionMessageBubbles for either extensions that look
-// suspicious and have therefore been disabled, or for extensions that are
-// running in developer mode that we want to warn the user about.
-class ExtensionMessageBubbleFactory {
- public:
-  explicit ExtensionMessageBubbleFactory(Profile* profile);
-  ~ExtensionMessageBubbleFactory();
-
-  // Returns the controller for the bubble that should be shown, if any.
-  scoped_ptr<extensions::ExtensionMessageBubbleController> GetController();
-
- private:
-  Profile* profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleFactory);
-};
-
-#endif  // CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_FACTORY_H_
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_ui.cc b/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
index 2f7f2cd9..a57b59f 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
+++ b/chrome/browser/ui/libgtk2ui/gtk2_ui.cc
@@ -380,16 +380,20 @@
   return params;
 }
 
-// Queries GTK for its font DPI setting and returns the number of pixels in a
-// point.
-double GetPixelsInPoint(float device_scale_factor) {
+double GetDPI() {
   GtkSettings* gtk_settings = gtk_settings_get_default();
   CHECK(gtk_settings);
   gint gtk_dpi = -1;
   g_object_get(gtk_settings, "gtk-xft-dpi", &gtk_dpi, NULL);
 
   // GTK multiplies the DPI by 1024 before storing it.
-  double dpi = (gtk_dpi > 0) ? gtk_dpi / 1024.0 : 96.0;
+  return (gtk_dpi > 0) ? gtk_dpi / 1024.0 : 96.0;
+}
+
+// Queries GTK for its font DPI setting and returns the number of pixels in a
+// point.
+double GetPixelsInPoint(float device_scale_factor) {
+  double dpi = GetDPI();
 
   // Take device_scale_factor into account — if Chrome already scales the
   // entire UI up by 2x, we should not also scale up.
@@ -1420,6 +1424,12 @@
   UpdateDefaultFont(label_style->font_desc);
 }
 
+float Gtk2UI::GetDeviceScaleFactor() const {
+  const int kCSSDefaultDPI = 96;
+  const float scale = GetDPI() / kCSSDefaultDPI;
+  return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale));
+}
+
 }  // namespace libgtk2ui
 
 views::LinuxUI* BuildGtk2UI() {
diff --git a/chrome/browser/ui/libgtk2ui/gtk2_ui.h b/chrome/browser/ui/libgtk2ui/gtk2_ui.h
index 209390d9..cd0918a7 100644
--- a/chrome/browser/ui/libgtk2ui/gtk2_ui.h
+++ b/chrome/browser/ui/libgtk2ui/gtk2_ui.h
@@ -121,6 +121,7 @@
 
   // ui::Views::LinuxUI:
   void UpdateDeviceScaleFactor(float device_scale_factor) override;
+  float GetDeviceScaleFactor() const override;
 
  private:
   typedef std::map<int, SkColor> ColorMap;
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index a3323ed..be3687c 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -7,14 +7,12 @@
 #include "base/auto_reset.h"
 #include "base/profiler/scoped_tracker.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_message_bubble_controller.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
-#include "chrome/browser/ui/extensions/extension_message_bubble_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
@@ -420,8 +418,7 @@
       model_observer_(this),
       suppress_layout_(false),
       suppress_animation_(true),
-      overflowed_action_wants_to_run_(false),
-      checked_extension_bubble_(false) {
+      overflowed_action_wants_to_run_(false) {
   if (model_)  // |model_| can be null in unittests.
     model_observer_.Add(model_);
 
@@ -634,11 +631,6 @@
 
   // Once the actions are created, we should animate the changes.
   suppress_animation_ = false;
-
-  // CreateActions() can be called multiple times, so we need to make sure we
-  // haven't already shown the bubble.
-  if (!checked_extension_bubble_)
-    MaybeShowExtensionBubble();
 }
 
 void ToolbarActionsBar::DeleteActions() {
@@ -737,16 +729,6 @@
   return true;
 }
 
-void ToolbarActionsBar::MaybeShowExtensionBubble() {
-  checked_extension_bubble_ = true;
-  scoped_ptr<extensions::ExtensionMessageBubbleController> controller =
-      ExtensionMessageBubbleFactory(browser_->profile()).GetController();
-  if (controller) {
-    controller->HighlightExtensionsIfNecessary();
-    delegate_->ShowExtensionMessageBubble(controller.Pass());
-  }
-}
-
 void ToolbarActionsBar::OnToolbarExtensionAdded(
     const extensions::Extension* extension,
     int index) {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.h b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
index 11a8902..3a6798f 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
@@ -198,9 +198,6 @@
   // Sets |overflowed_action_wants_to_run_| to the proper value.
   void SetOverflowedActionWantsToRun();
 
-  // Shows an extension message bubble, if any should be shown.
-  void MaybeShowExtensionBubble();
-
   bool in_overflow_mode() const { return main_bar_ != nullptr; }
 
   // The delegate for this object (in a real build, this is the view).
@@ -250,10 +247,6 @@
   // True if an action in the overflow menu wants to run.
   bool overflowed_action_wants_to_run_;
 
-  // True if we have checked to see if there is an extension bubble that should
-  // be displayed, and, if there is, shown that bubble.
-  bool checked_extension_bubble_;
-
   DISALLOW_COPY_AND_ASSIGN(ToolbarActionsBar);
 };
 
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h b/chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h
index af71316..9a24e6d 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h
@@ -5,16 +5,11 @@
 #ifndef CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_DELEGATE_H_
 #define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_DELEGATE_H_
 
-#include "base/memory/scoped_ptr.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/size.h"
 
 class ToolbarActionViewController;
 
-namespace extensions {
-class ExtensionMessageBubbleController;
-}
-
 // The delegate class (which, in production, represents the view) of the
 // ToolbarActionsBar.
 class ToolbarActionsBarDelegate {
@@ -64,10 +59,6 @@
   // action wants to run has changed.
   virtual void OnOverflowedActionWantsToRunChanged(
       bool overflowed_action_wants_to_run) = 0;
-
-  // Displays the bubble for the passed ExtensionMessageBubbleController.
-  virtual void ShowExtensionMessageBubble(
-      scoped_ptr<extensions::ExtensionMessageBubbleController> controller) = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_BAR_DELEGATE_H_
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc
index a2b70d47..578d693 100644
--- a/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view.cc
@@ -7,9 +7,24 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/dev_mode_bubble_controller.h"
+#include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/extensions/extension_message_bubble_controller.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_toolbar_model.h"
+#include "chrome/browser/extensions/proxy_overridden_bubble_controller.h"
+#include "chrome/browser/extensions/settings_api_bubble_controller.h"
+#include "chrome/browser/extensions/settings_api_helpers.h"
+#include "chrome/browser/extensions/suspicious_extension_bubble_controller.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
+#include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/grit/locale_settings.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
 #include "ui/accessibility/ax_view_state.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/views/controls/button/label_button.h"
@@ -21,6 +36,9 @@
 
 namespace {
 
+base::LazyInstance<std::set<Profile*> > g_profiles_evaluated =
+    LAZY_INSTANCE_INITIALIZER;
+
 // Layout constants.
 const int kExtensionListPadding = 10;
 const int kInsetBottomRight = 13;
@@ -61,6 +79,21 @@
   set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
 }
 
+void ExtensionMessageBubbleView::OnActionButtonClicked(
+    const base::Closure& callback) {
+  action_callback_ = callback;
+}
+
+void ExtensionMessageBubbleView::OnDismissButtonClicked(
+    const base::Closure& callback) {
+  dismiss_callback_ = callback;
+}
+
+void ExtensionMessageBubbleView::OnLinkClicked(
+    const base::Closure& callback) {
+  link_callback_ = callback;
+}
+
 void ExtensionMessageBubbleView::Show() {
   // Not showing the bubble right away (during startup) has a few benefits:
   // We don't have to worry about focus being lost due to the Omnibox (or to
@@ -81,7 +114,7 @@
   // To catch Esc, we monitor destroy message. Unless the link has been clicked,
   // we assume Dismiss was the action taken.
   if (!link_clicked_ && !action_taken_)
-    controller_->OnBubbleDismiss();
+    dismiss_callback_.Run();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -208,7 +241,7 @@
                                                const ui::Event& event) {
   if (sender == action_button_) {
     action_taken_ = true;
-    controller_->OnBubbleAction();
+    action_callback_.Run();
   } else {
     DCHECK_EQ(dismiss_button_, sender);
   }
@@ -219,7 +252,7 @@
                                              int event_flags) {
   DCHECK_EQ(learn_more_, source);
   link_clicked_ = true;
-  controller_->OnLinkClicked();
+  link_callback_.Run();
   GetWidget()->Close();
 }
 
@@ -234,4 +267,243 @@
     NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// ExtensionMessageBubbleFactory
+
+ExtensionMessageBubbleFactory::ExtensionMessageBubbleFactory(
+    Profile* profile,
+    ToolbarView* toolbar_view)
+    : profile_(profile),
+      toolbar_view_(toolbar_view),
+      shown_suspicious_extensions_bubble_(false),
+      shown_startup_override_extensions_bubble_(false),
+      shown_proxy_override_extensions_bubble_(false),
+      shown_dev_mode_extensions_bubble_(false),
+      is_observing_(false),
+      stage_(STAGE_START),
+      container_(NULL),
+      anchor_view_(NULL) {}
+
+ExtensionMessageBubbleFactory::~ExtensionMessageBubbleFactory() {
+  MaybeStopObserving();
+}
+
+void ExtensionMessageBubbleFactory::MaybeShow(views::View* anchor_view) {
+#if defined(OS_WIN)
+  bool is_initial_check = IsInitialProfileCheck(profile_->GetOriginalProfile());
+  RecordProfileCheck(profile_->GetOriginalProfile());
+
+  // The list of suspicious extensions takes priority over the dev mode bubble
+  // and the settings API bubble, since that needs to be shown as soon as we
+  // disable something. The settings API bubble is shown on first startup after
+  // an extension has changed the startup pages and it is acceptable if that
+  // waits until the next startup because of the suspicious extension bubble.
+  // The dev mode bubble is not time sensitive like the other two so we'll catch
+  // the dev mode extensions on the next startup/next window that opens. That
+  // way, we're not too spammy with the bubbles.
+  if (!shown_suspicious_extensions_bubble_ &&
+      MaybeShowSuspiciousExtensionsBubble(anchor_view))
+    return;
+
+  if (!shown_startup_override_extensions_bubble_ &&
+      is_initial_check &&
+      MaybeShowStartupOverrideExtensionsBubble(anchor_view))
+    return;
+
+  if (!shown_proxy_override_extensions_bubble_ &&
+      MaybeShowProxyOverrideExtensionsBubble(anchor_view))
+    return;
+
+  if (!shown_dev_mode_extensions_bubble_)
+    MaybeShowDevModeExtensionsBubble(anchor_view);
+#endif  // OS_WIN
+}
+
+bool ExtensionMessageBubbleFactory::MaybeShowSuspiciousExtensionsBubble(
+    views::View* anchor_view) {
+  DCHECK(!shown_suspicious_extensions_bubble_);
+
+  scoped_ptr<SuspiciousExtensionBubbleController> suspicious_extensions(
+      new SuspiciousExtensionBubbleController(profile_));
+  if (!suspicious_extensions->ShouldShow())
+    return false;
+
+  shown_suspicious_extensions_bubble_ = true;
+  SuspiciousExtensionBubbleController* weak_controller =
+      suspicious_extensions.get();
+  ExtensionMessageBubbleView* bubble_delegate =
+      new ExtensionMessageBubbleView(anchor_view,
+                                     views::BubbleBorder::TOP_RIGHT,
+                                     suspicious_extensions.Pass());
+
+  views::BubbleDelegateView::CreateBubble(bubble_delegate);
+  weak_controller->Show(bubble_delegate);
+
+  return true;
+}
+
+bool ExtensionMessageBubbleFactory::MaybeShowStartupOverrideExtensionsBubble(
+    views::View* anchor_view) {
+#if !defined(OS_WIN)
+  return false;
+#else
+  DCHECK(!shown_startup_override_extensions_bubble_);
+
+  const Extension* extension = GetExtensionOverridingStartupPages(profile_);
+  if (!extension)
+    return false;
+
+  scoped_ptr<SettingsApiBubbleController> settings_api_bubble(
+      new SettingsApiBubbleController(profile_,
+                                      BUBBLE_TYPE_STARTUP_PAGES));
+  if (!settings_api_bubble->ShouldShow(extension->id()))
+    return false;
+
+  shown_startup_override_extensions_bubble_ = true;
+  PrepareToHighlightExtensions(settings_api_bubble.Pass(), anchor_view);
+  return true;
+#endif
+}
+
+bool ExtensionMessageBubbleFactory::MaybeShowProxyOverrideExtensionsBubble(
+    views::View* anchor_view) {
+#if !defined(OS_WIN)
+  return false;
+#else
+  DCHECK(!shown_proxy_override_extensions_bubble_);
+
+  const Extension* extension = GetExtensionOverridingProxy(profile_);
+  if (!extension)
+    return false;
+
+  scoped_ptr<ProxyOverriddenBubbleController> proxy_bubble(
+      new ProxyOverriddenBubbleController(profile_));
+  if (!proxy_bubble->ShouldShow(extension->id()))
+    return false;
+
+  shown_proxy_override_extensions_bubble_ = true;
+  PrepareToHighlightExtensions(proxy_bubble.Pass(), anchor_view);
+  return true;
+#endif
+}
+
+bool ExtensionMessageBubbleFactory::MaybeShowDevModeExtensionsBubble(
+    views::View* anchor_view) {
+  DCHECK(!shown_dev_mode_extensions_bubble_);
+
+  // Check the Developer Mode extensions.
+  scoped_ptr<DevModeBubbleController> dev_mode_extensions(
+      new DevModeBubbleController(profile_));
+
+  // Return early if we have none to show.
+  if (!dev_mode_extensions->ShouldShow())
+    return false;
+
+  shown_dev_mode_extensions_bubble_ = true;
+  PrepareToHighlightExtensions(dev_mode_extensions.Pass(), anchor_view);
+  return true;
+}
+
+void ExtensionMessageBubbleFactory::MaybeObserve() {
+  if (!is_observing_) {
+    is_observing_ = true;
+    container_->AddObserver(this);
+  }
+}
+
+void ExtensionMessageBubbleFactory::MaybeStopObserving() {
+  if (is_observing_) {
+    is_observing_ = false;
+    container_->RemoveObserver(this);
+  }
+}
+
+void ExtensionMessageBubbleFactory::RecordProfileCheck(Profile* profile) {
+  g_profiles_evaluated.Get().insert(profile);
+}
+
+bool ExtensionMessageBubbleFactory::IsInitialProfileCheck(Profile* profile) {
+  return g_profiles_evaluated.Get().count(profile) == 0;
+}
+
+void ExtensionMessageBubbleFactory::OnBrowserActionsContainerAnimationEnded() {
+  MaybeStopObserving();
+  if (stage_ == STAGE_START) {
+    HighlightExtensions();
+  } else if (stage_ == STAGE_HIGHLIGHTED) {
+    ShowHighlightingBubble();
+  } else {  // We shouldn't be observing if we've completed the process.
+    NOTREACHED();
+    Finish();
+  }
+}
+
+void ExtensionMessageBubbleFactory::OnBrowserActionsContainerDestroyed() {
+  // If the container associated with the bubble is destroyed, abandon the
+  // process.
+  Finish();
+}
+
+void ExtensionMessageBubbleFactory::PrepareToHighlightExtensions(
+    scoped_ptr<ExtensionMessageBubbleController> controller,
+    views::View* anchor_view) {
+  // We should be in the start stage (i.e., should not have a pending attempt to
+  // show a bubble).
+  DCHECK_EQ(stage_, STAGE_START);
+
+  // Prepare to display and highlight the extensions before showing the bubble.
+  // Since this is an asynchronous process, set member variables for later use.
+  controller_ = controller.Pass();
+  anchor_view_ = anchor_view;
+  container_ = toolbar_view_->browser_actions();
+
+  if (container_->animating())
+    MaybeObserve();
+  else
+    HighlightExtensions();
+}
+
+void ExtensionMessageBubbleFactory::HighlightExtensions() {
+  DCHECK_EQ(STAGE_START, stage_);
+  stage_ = STAGE_HIGHLIGHTED;
+
+  const ExtensionIdList extension_list = controller_->GetExtensionIdList();
+  DCHECK(!extension_list.empty());
+  ExtensionToolbarModel::Get(profile_)->HighlightExtensions(extension_list);
+  if (container_->animating())
+    MaybeObserve();
+  else
+    ShowHighlightingBubble();
+}
+
+void ExtensionMessageBubbleFactory::ShowHighlightingBubble() {
+  DCHECK_EQ(stage_, STAGE_HIGHLIGHTED);
+  stage_ = STAGE_COMPLETE;
+
+  views::View* reference_view = NULL;
+  if (container_->num_toolbar_actions() > 0u)
+    reference_view = container_->GetToolbarActionViewAt(0);
+  if (reference_view && reference_view->visible())
+    anchor_view_ = reference_view;
+
+  ExtensionMessageBubbleController* weak_controller = controller_.get();
+  ExtensionMessageBubbleView* bubble_delegate =
+      new ExtensionMessageBubbleView(
+          anchor_view_,
+          views::BubbleBorder::TOP_RIGHT,
+          scoped_ptr<ExtensionMessageBubbleController>(
+              controller_.release()));
+  views::BubbleDelegateView::CreateBubble(bubble_delegate);
+  weak_controller->Show(bubble_delegate);
+
+  Finish();
+}
+
+void ExtensionMessageBubbleFactory::Finish() {
+  MaybeStopObserving();
+  controller_.reset();
+  anchor_view_ = NULL;
+  container_ = NULL;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view.h b/chrome/browser/ui/views/extensions/extension_message_bubble_view.h
index 177673f..eadaf716 100644
--- a/chrome/browser/ui/views/extensions/extension_message_bubble_view.h
+++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view.h
@@ -5,14 +5,18 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_MESSAGE_BUBBLE_VIEW_H_
 
+#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/extensions/extension_message_bubble.h"
+#include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
 #include "ui/views/bubble/bubble_delegate.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/link_listener.h"
 
 class Profile;
+class BrowserActionsContainer;
+class ToolbarView;
 
 namespace views {
 class Label;
@@ -22,8 +26,121 @@
 
 namespace extensions {
 
+class DevModeBubbleController;
 class ExtensionMessageBubbleController;
 
+// Create and show ExtensionMessageBubbles for either extensions that look
+// suspicious and have therefore been disabled, or for extensions that are
+// running in developer mode that we want to warn the user about.
+// Calling MaybeShow() will show one of the bubbles, if there is cause to (we
+// don't show both in order to avoid spamminess). The suspicious extensions
+// bubble takes priority over the developer mode extensions bubble.
+class ExtensionMessageBubbleFactory : public BrowserActionsContainerObserver {
+ public:
+  ExtensionMessageBubbleFactory(Profile* profile, ToolbarView* toolbar_view);
+  ~ExtensionMessageBubbleFactory() override;
+
+  void MaybeShow(views::View* anchor_view);
+
+ private:
+  // The stage of showing the developer mode extensions bubble. STAGE_START
+  // corresponds to the beginning of the process, when nothing has been done.
+  // STAGE_HIGHLIGHTED indicates that the toolbar should be highlighting
+  // dangerous extensions. STAGE_COMPLETE means that the process should be
+  // ended.
+  enum Stage { STAGE_START, STAGE_HIGHLIGHTED, STAGE_COMPLETE };
+
+  // Shows the suspicious extensions bubble, if there are suspicious extensions
+  // and we have not done so already.
+  // Returns true if we have show the view.
+  bool MaybeShowSuspiciousExtensionsBubble(views::View* anchor_view);
+
+  // Shows the settings API extensions bubble, if there are extensions
+  // overriding the startup pages and we have not done so already.
+  // Returns true if we show the view (or start the process).
+  bool MaybeShowStartupOverrideExtensionsBubble(views::View* anchor_view);
+
+  // Shows the bubble for when there are extensions overriding the proxy (if we
+  // have not done so already). Returns true if we show the view (or start the
+  // process of doing so).
+  bool MaybeShowProxyOverrideExtensionsBubble(views::View* anchor_view);
+
+  // Shows the developer mode extensions bubble, if there are extensions running
+  // in developer mode and we have not done so already.
+  // Returns true if we show the view (or start the process).
+  bool MaybeShowDevModeExtensionsBubble(views::View* anchor_view);
+
+  // Starts or stops observing the BrowserActionsContainer, if necessary.
+  void MaybeObserve();
+  void MaybeStopObserving();
+
+  // Adds |profile| to the list of profiles that have been evaluated for showing
+  // a bubble. Handy for things that only want to check once per profile.
+  void RecordProfileCheck(Profile* profile);
+  // Returns false if this profile has been evaluated before.
+  bool IsInitialProfileCheck(Profile* profile);
+
+  // BrowserActionsContainer::Observer implementation.
+  void OnBrowserActionsContainerAnimationEnded() override;
+  void OnBrowserActionsContainerDestroyed() override;
+
+  // Sets the stage for highlighting extensions and then showing the bubble
+  // controlled by |controller|, anchored to |anchor_view|.
+  void PrepareToHighlightExtensions(
+      scoped_ptr<ExtensionMessageBubbleController> controller,
+      views::View* anchor_view);
+
+  // Inform the ExtensionToolbarModel to highlight the appropriate extensions.
+  void HighlightExtensions();
+
+  // Shows the waiting bubbble, after highlighting the extensions.
+  void ShowHighlightingBubble();
+
+  // Finishes the process of showing the developer mode bubble.
+  void Finish();
+
+  // The associated profile.
+  Profile* profile_;
+
+  // The toolbar view that the ExtensionMessageBubbleViews will attach to.
+  ToolbarView* toolbar_view_;
+
+  // Whether or not we have shown the suspicious extensions bubble.
+  bool shown_suspicious_extensions_bubble_;
+
+  // Whether or not we have shown the Settings API extensions bubble notifying
+  // the user about the startup pages being overridden.
+  bool shown_startup_override_extensions_bubble_;
+
+  // Whether or not we have shown the bubble notifying the user about the proxy
+  // being overridden.
+  bool shown_proxy_override_extensions_bubble_;
+
+  // Whether or not we have shown the developer mode extensions bubble.
+  bool shown_dev_mode_extensions_bubble_;
+
+  // Whether or not we are already observing the BrowserActionsContainer (so
+  // we don't add ourselves twice).
+  bool is_observing_;
+
+  // The current stage of showing the bubble.
+  Stage stage_;
+
+  // The BrowserActionsContainer for the profile. This will be NULL if the
+  // factory is not currently in the process of showing a bubble.
+  BrowserActionsContainer* container_;
+
+  // The default view to anchor the bubble to. This will be NULL if the factory
+  // is not currently in the process of showing a bubble.
+  views::View* anchor_view_;
+
+  // The controller to show a bubble for. This will be NULL if the factory is
+  // not currently in the process of showing a bubble.
+  scoped_ptr<ExtensionMessageBubbleController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleFactory);
+};
+
 // This is a class that implements the UI for the bubble showing which
 // extensions look suspicious and have therefore been automatically disabled.
 class ExtensionMessageBubbleView : public ExtensionMessageBubble,
@@ -37,6 +154,9 @@
       scoped_ptr<ExtensionMessageBubbleController> controller);
 
   // ExtensionMessageBubble methods.
+  void OnActionButtonClicked(const base::Closure& callback) override;
+  void OnDismissButtonClicked(const base::Closure& callback) override;
+  void OnLinkClicked(const base::Closure& callback) override;
   void Show() override;
 
   // WidgetObserver methods.
@@ -78,6 +198,11 @@
   bool link_clicked_;
   bool action_taken_;
 
+  // Callbacks into the controller.
+  base::Closure action_callback_;
+  base::Closure dismiss_callback_;
+  base::Closure link_callback_;
+
   base::WeakPtrFactory<ExtensionMessageBubbleView> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleView);
diff --git a/chrome/browser/ui/views/settings_api_bubble_helper_views.cc b/chrome/browser/ui/views/settings_api_bubble_helper_views.cc
index 06bb439..341dc6124 100644
--- a/chrome/browser/ui/views/settings_api_bubble_helper_views.cc
+++ b/chrome/browser/ui/views/settings_api_bubble_helper_views.cc
@@ -25,12 +25,13 @@
 namespace {
 
 void ShowSettingsApiBubble(SettingsApiOverrideType type,
+                           const std::string& extension_id,
                            Profile* profile,
                            views::View* anchor_view,
                            views::BubbleBorder::Arrow arrow) {
   scoped_ptr<SettingsApiBubbleController> settings_api_bubble(
       new SettingsApiBubbleController(profile, type));
-  if (!settings_api_bubble->ShouldShow())
+  if (!settings_api_bubble->ShouldShow(extension_id))
     return;
 
   SettingsApiBubbleController* controller = settings_api_bubble.get();
@@ -47,13 +48,18 @@
   return;
 #endif
 
-  // The bubble will try to anchor itself against the home button
-  views::View* anchor_view = BrowserView::GetBrowserViewForBrowser(browser)->
-      toolbar()->home_button();
-  ShowSettingsApiBubble(BUBBLE_TYPE_HOME_PAGE,
-                        browser->profile(),
-                        anchor_view,
-                        views::BubbleBorder::TOP_LEFT);
+  const Extension* extension =
+      GetExtensionOverridingHomepage(browser->profile());
+  if (extension) {
+    // The bubble will try to anchor itself against the home button
+    views::View* anchor_view = BrowserView::GetBrowserViewForBrowser(browser)->
+        toolbar()->home_button();
+    ShowSettingsApiBubble(BUBBLE_TYPE_HOME_PAGE,
+                          extension->id(),
+                          browser->profile(),
+                          anchor_view,
+                          views::BubbleBorder::TOP_LEFT);
+  }
 }
 
 void MaybeShowExtensionControlledSearchNotification(
@@ -66,13 +72,17 @@
 
   if (AutocompleteMatch::IsSearchType(match.type) &&
       match.type != AutocompleteMatchType::SEARCH_OTHER_ENGINE) {
-    ToolbarView* toolbar =
-        BrowserView::GetBrowserViewForBrowser(
-            chrome::FindBrowserWithWebContents(web_contents))->toolbar();
-    ShowSettingsApiBubble(BUBBLE_TYPE_SEARCH_ENGINE,
-                          profile,
-                          toolbar->app_menu(),
-                          views::BubbleBorder::TOP_RIGHT);
+    const Extension* extension = GetExtensionOverridingSearchEngine(profile);
+    if (extension) {
+      ToolbarView* toolbar =
+          BrowserView::GetBrowserViewForBrowser(
+              chrome::FindBrowserWithWebContents(web_contents))->toolbar();
+      ShowSettingsApiBubble(BUBBLE_TYPE_SEARCH_ENGINE,
+                            extension->id(),
+                            profile,
+                            toolbar->app_menu(),
+                            views::BubbleBorder::TOP_RIGHT);
+    }
   }
 }
 
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index 84deb1a9..b86b084 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -6,7 +6,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/stl_util.h"
-#include "chrome/browser/extensions/extension_message_bubble_controller.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -15,7 +14,6 @@
 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
-#include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
 #include "chrome/browser/ui/views/extensions/extension_toolbar_icon_surfacing_bubble_views.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
@@ -341,30 +339,6 @@
           overflowed_action_wants_to_run);
 }
 
-void BrowserActionsContainer::ShowExtensionMessageBubble(
-    scoped_ptr<extensions::ExtensionMessageBubbleController> controller) {
-  if (animating()) {
-    // If the container is animating, we can't effectively anchor the bubble,
-    // so wait until animation stops.
-    pending_extension_bubble_controller_ = controller.Pass();
-    return;
-  }
-
-  views::View* reference_view = VisibleBrowserActions() > 0 ?
-      static_cast<views::View*>(toolbar_action_views_[0]) :
-      BrowserView::GetBrowserViewForBrowser(browser_)->toolbar()->app_menu();
-
-  extensions::ExtensionMessageBubbleController* weak_controller =
-      controller.get();
-  extensions::ExtensionMessageBubbleView* bubble =
-      new extensions::ExtensionMessageBubbleView(
-          reference_view,
-          views::BubbleBorder::TOP_RIGHT,
-          controller.Pass());
-  views::BubbleDelegateView::CreateBubble(bubble);
-  weak_controller->Show(bubble);
-}
-
 void BrowserActionsContainer::AddObserver(
     BrowserActionsContainerObserver* observer) {
   observers_.AddObserver(observer);
@@ -694,9 +668,6 @@
   FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
                     observers_,
                     OnBrowserActionsContainerAnimationEnded());
-
-  if (pending_extension_bubble_controller_)
-    ShowExtensionMessageBubble(pending_extension_bubble_controller_.Pass());
 }
 
 content::WebContents* BrowserActionsContainer::GetCurrentWebContents() {
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.h b/chrome/browser/ui/views/toolbar/browser_actions_container.h
index 3b26802..ab40e10 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.h
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.h
@@ -252,9 +252,6 @@
   bool IsPopupRunning() const override;
   void OnOverflowedActionWantsToRunChanged(
       bool overflowed_action_wants_to_run) override;
-  void ShowExtensionMessageBubble(
-      scoped_ptr<extensions::ExtensionMessageBubbleController> controller)
-          override;
 
   // Overridden from extension::ExtensionKeybindingRegistry::Delegate:
   extensions::ActiveTabPermissionGranter* GetActiveTabPermissionGranter()
@@ -345,10 +342,6 @@
   // The class that registers for keyboard shortcuts for extension commands.
   scoped_ptr<ExtensionKeybindingRegistryViews> extension_keybinding_registry_;
 
-  // The controller of the bubble to show once animation finishes, if any.
-  scoped_ptr<extensions::ExtensionMessageBubbleController>
-      pending_extension_bubble_controller_;
-
   ObserverList<BrowserActionsContainerObserver> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index df70ca44a..7c4073b 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
 #include "chrome/browser/ui/views/extensions/extension_popup.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
@@ -129,7 +130,10 @@
       browser_actions_(NULL),
       app_menu_(NULL),
       browser_(browser),
-      badge_controller_(browser->profile(), this) {
+      badge_controller_(browser->profile(), this),
+      extension_message_bubble_factory_(
+          new extensions::ExtensionMessageBubbleFactory(browser->profile(),
+                                                        this)) {
   set_id(VIEW_ID_TOOLBAR);
 
   SetEventTargeter(
@@ -267,6 +271,14 @@
   }
 }
 
+void ToolbarView::OnWidgetVisibilityChanged(views::Widget* widget,
+                                            bool visible) {
+  if (visible) {
+    // Safe to call multiple times; the bubble will only appear once.
+    extension_message_bubble_factory_->MaybeShow(app_menu_);
+  }
+}
+
 void ToolbarView::OnWidgetActivationChanged(views::Widget* widget,
                                             bool active) {
   extensions::ExtensionCommandsGlobalRegistry* registry =
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index 0e60a16..32fe3f6 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -31,6 +31,7 @@
 namespace extensions {
 class Command;
 class Extension;
+class ExtensionMessageBubbleFactory;
 }
 
 namespace views {
@@ -136,6 +137,7 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
   // views::WidgetObserver:
+  void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
 
   // content::NotificationObserver:
@@ -243,6 +245,11 @@
   scoped_ptr<WrenchMenuModel> wrench_menu_model_;
   scoped_ptr<WrenchMenu> wrench_menu_;
 
+  // The factory to create bubbles to warn about dangerous/suspicious
+  // extensions.
+  scoped_ptr<extensions::ExtensionMessageBubbleFactory>
+      extension_message_bubble_factory_;
+
   // A list of listeners to call when the menu opens.
   ObserverList<views::MenuListener> menu_listeners_;
 
diff --git a/chrome/browser/ui/webui/large_icon_source.cc b/chrome/browser/ui/webui/large_icon_source.cc
index 56e34613..df1e285f 100644
--- a/chrome/browser/ui/webui/large_icon_source.cc
+++ b/chrome/browser/ui/webui/large_icon_source.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/webui/large_icon_source.h"
 
-#include <vector>
-
 #include "base/memory/ref_counted_memory.h"
 #include "chrome/browser/search/instant_io_context.h"
 #include "chrome/common/favicon/large_icon_url_parser.h"
@@ -15,11 +13,18 @@
 #include "components/favicon_base/fallback_icon_style.h"
 #include "net/url_request/url_request.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_analysis.h"
+#include "ui/gfx/color_utils.h"
 
 namespace {
 
-int kDefaultLargeIconSize = 96;
-int kMaxLargeIconSize = 192;  // Arbitrary bound to safeguard endpoint.
+const int kDefaultLargeIconSize = 96;
+const int kMaxLargeIconSize = 192;  // Arbitrary bound to safeguard endpoint.
+
+const double kMaxBackgroundLuminance = 0.67;
+const SkColor kDarkGray = SkColorSetRGB(0x78, 0x78, 0x78);
+const SkColor kTextColor = SK_ColorWHITE;
+const SkColor kDefaultBackgroundColor = kDarkGray;
 
 }  // namespace
 
@@ -43,6 +48,9 @@
     favicon::FallbackIconService* fallback_icon_service)
     : favicon_service_(favicon_service),
       fallback_icon_service_(fallback_icon_service) {
+  large_icon_types_.push_back(favicon_base::IconType::FAVICON);
+  large_icon_types_.push_back(favicon_base::IconType::TOUCH_ICON);
+  large_icon_types_.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON);
 }
 
 LargeIconSource::~LargeIconSource() {
@@ -77,9 +85,9 @@
     return;
   }
 
-  favicon_service_->GetRawFaviconForPageURL(
+  favicon_service_->GetLargestRawFaviconForPageURL(
       url,
-      favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON,
+      large_icon_types_,
       parser.size_in_pixels(),
       base::Bind(
           &LargeIconSource::OnIconDataAvailable,
@@ -110,20 +118,45 @@
 void LargeIconSource::OnIconDataAvailable(
     const IconRequest& request,
     const favicon_base::FaviconRawBitmapResult& bitmap_result) {
-  if (!bitmap_result.is_valid())
-    SendFallbackIcon(request);
-  else
-    request.callback.Run(bitmap_result.bitmap_data.get());
+  if (!bitmap_result.is_valid()) {
+    SendDefaultFallbackIcon(request);
+    return;
+  }
+
+  // If we found a bitmap, but it's smaller than the requested size, we
+  // generate a fallback using the dominant color from the too-small bitmap.
+  // We adjust the luminance of the background so we can put light text over it.
+  if (bitmap_result.pixel_size.width() < request.size ||
+      bitmap_result.pixel_size.height() < request.size) {
+    SkColor background =
+        color_utils::CalculateKMeanColorOfPNG(bitmap_result.bitmap_data);
+    color_utils::HSL background_hsl;
+    color_utils::SkColorToHSL(background, &background_hsl);
+    background_hsl.l = std::min(background_hsl.l, kMaxBackgroundLuminance);
+    background = color_utils::HSLToSkColor(background_hsl, SK_AlphaOPAQUE);
+
+    // Now we can construct the fallback icon.
+    SendFallbackIcon(request, kTextColor, background);
+    return;
+  }
+
+  request.callback.Run(bitmap_result.bitmap_data.get());
 }
 
-void LargeIconSource::SendFallbackIcon(const IconRequest& request) {
+void LargeIconSource::SendDefaultFallbackIcon(const IconRequest& request) {
+  SendFallbackIcon(request, kTextColor, kDefaultBackgroundColor);
+}
+
+void LargeIconSource::SendFallbackIcon(const IconRequest& request,
+                                       SkColor text_color,
+                                       SkColor background_color) {
   if (!fallback_icon_service_) {
     SendNotFoundResponse(request.callback);
     return;
   }
   favicon_base::FallbackIconStyle style;
-  style.background_color = SkColorSetRGB(0x78, 0x78, 0x78);
-  style.text_color = SK_ColorWHITE;
+  style.background_color = background_color;
+  style.text_color = text_color;
   style.font_size_ratio = 0.44;
   style.roundness = 0;  // Square. Round corners can be applied by JavaScript.
   std::vector<unsigned char> bitmap_data =
diff --git a/chrome/browser/ui/webui/large_icon_source.h b/chrome/browser/ui/webui/large_icon_source.h
index 7557cfd..05872d2 100644
--- a/chrome/browser/ui/webui/large_icon_source.h
+++ b/chrome/browser/ui/webui/large_icon_source.h
@@ -6,12 +6,14 @@
 #define CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
 
 #include <string>
+#include <vector>
 
 #include "base/memory/scoped_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/fallback_icon_service.h"
 #include "components/favicon_base/favicon_types.h"
 #include "content/public/browser/url_data_source.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace favicon {
 class FallbackIconService;
@@ -70,8 +72,15 @@
       const IconRequest& request,
       const favicon_base::FaviconRawBitmapResult& bitmap_result);
 
-  // Renders and sends a fallback icon.
-  void SendFallbackIcon(const IconRequest& request);
+  // Renders and sends a default fallback icon. This is used when there is no
+  // known text and/or foreground color to use for the generated icon (it
+  // defaults to a light text color on a dark gray background).
+  void SendDefaultFallbackIcon(const IconRequest& request);
+
+  // Renders and sends a fallback icon using the given colors.
+  void SendFallbackIcon(const IconRequest& request,
+                        SkColor text_color,
+                        SkColor background_color);
 
   // Returns null to trigger "Not Found" response.
   void SendNotFoundResponse(
@@ -83,6 +92,12 @@
 
   favicon::FallbackIconService* fallback_icon_service_;
 
+  // A pre-populated list of the types of icon files to consider when looking
+  // for the largest matching icon.
+  // Note: this is simply an optimization over populating an icon type vector
+  //     on each request.
+  std::vector<int> large_icon_types_;
+
   DISALLOW_COPY_AND_ASSIGN(LargeIconSource);
 };
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 9bcb16b4c..7a2b2c7 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -85,6 +85,13 @@
       IDS_SETTINGS_DOWNLOADS_PROMPT_FOR_DOWNLOAD_LABEL);
 }
 
+#if defined(OS_CHROMEOS)
+void AddInternetStrings(content::WebUIDataSource* html_source) {
+  html_source->AddLocalizedString(
+      "internetPageTitle", IDS_SETTINGS_INTERNET_PAGE_TITLE);
+}
+#endif
+
 }  // namespace
 
 namespace settings {
@@ -92,7 +99,9 @@
 void AddLocalizedStrings(content::WebUIDataSource* html_source) {
   AddA11yStrings(html_source);
   AddDownloadsStrings(html_source);
-
+#if defined(OS_CHROMEOS)
+  AddInternetStrings(html_source);
+#endif
   html_source->SetJsonPath(kLocalizedStringsFile);
 }
 
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index c5eff40..6a254de06 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -20,6 +20,8 @@
     'chrome_browser_non_ios_sources': [
       'browser/about_flags.cc',
       'browser/about_flags.h',
+      'browser/after_startup_task_utils.cc',
+      'browser/after_startup_task_utils.h',
       'browser/android/accessibility/font_size_prefs_android.cc',
       'browser/android/accessibility/font_size_prefs_android.h',
       'browser/android/accessibility_util.cc',
@@ -304,6 +306,8 @@
       'browser/component_updater/sw_reporter_installer_win.h',
       'browser/component_updater/swiftshader_component_installer.cc',
       'browser/component_updater/swiftshader_component_installer.h',
+      'browser/component_updater/url_constants.cc',
+      'browser/component_updater/url_constants.h',
       'browser/crash_upload_list.cc',
       'browser/crash_upload_list.h',
       'browser/crash_upload_list_mac.cc',
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index f8dc319..ff08a47 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -357,6 +357,8 @@
         'browser/chromeos/fileapi/file_system_backend_delegate.h',
         'browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc',
         'browser/chromeos/fileapi/mtp_file_system_backend_delegate.h',
+        'browser/chromeos/fileapi/mtp_watcher_manager.cc',
+        'browser/chromeos/fileapi/mtp_watcher_manager.h',
         'browser/chromeos/first_run/drive_first_run_controller.cc',
         'browser/chromeos/first_run/drive_first_run_controller.h',
         'browser/chromeos/first_run/first_run.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index fe5259fd..5e6f594 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -2592,8 +2592,6 @@
       'browser/ui/extensions/extension_action_platform_delegate.h',
       'browser/ui/extensions/extension_action_view_controller.cc',
       'browser/ui/extensions/extension_action_view_controller.h',
-      'browser/ui/extensions/extension_message_bubble_factory.cc',
-      'browser/ui/extensions/extension_message_bubble_factory.h',
       'browser/ui/extensions/extension_enable_flow.cc',
       'browser/ui/extensions/extension_enable_flow.h',
       'browser/ui/extensions/extension_enable_flow_delegate.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 427f2c16..e29e7ba 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -14,6 +14,7 @@
       '../tools/metrics/histograms/histograms.xml',
       # All unittests in browser, common, renderer and service.
       'browser/about_flags_unittest.cc',
+      'browser/after_startup_task_utils_unittest.cc',
       'browser/android/bookmarks/partner_bookmarks_shim_unittest.cc',
       # TODO(newt): move this to test_support_unit?
       'browser/android/mock_location_settings.cc',
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 9465fa4..947666f0 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -109,6 +109,9 @@
       "//extensions/strings",
       "//media/cast:net",
     ]
+    if (enable_media_router) {
+      defines += [ "ENABLE_MEDIA_ROUTER=1" ]
+    }
   }
 
   if (is_win || is_mac) {
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 41dfa45..2411fa2a 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -50,6 +50,9 @@
 const char kSettingsAppId[] = "ennkphjdgehloodpbhlhldgbnhmacadg";
 const char kYoutubeAppId[] = "blpcfgokakmgnkcojhhkbfbldkacnbeo";
 const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda";
+#if defined(ENABLE_MEDIA_ROUTER)
+const char kMediaRouterStableExtensionId[] = "fjhoaacokmgbjemoflkofnenfaiekifl";
+#endif  // defined(ENABLE_MEDIA_ROUTER)
 
 #if defined(OS_CHROMEOS)
 // The extension id for the built-in component extension.
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index b368a7d4..46226be 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -94,6 +94,11 @@
 // The extension id of the in-app payments support application.
 extern const char kInAppPaymentsSupportAppId[];
 
+#if defined(ENABLE_MEDIA_ROUTER)
+// The extension id of the stable media router extension.
+extern const char kMediaRouterStableExtensionId[];
+#endif  // defined(ENABLE_MEDIA_ROUTER)
+
 // The buckets used for app launches.
 enum AppLaunchBucket {
   // Launch from NTP apps section while maximized.
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index b8e0e74..2966dbf9 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1619,6 +1619,9 @@
 // A dictionary of port->location pairs for port forwarding.
 const char kDevToolsPortForwardingConfig[] = "devtools.port_forwarding_config";
 
+// A dictionary with generic DevTools settings.
+const char kDevToolsPreferences[] = "devtools.preferences";
+
 #if defined(OS_ANDROID)
 // A boolean specifying whether remote dev tools debugging is enabled.
 const char kDevToolsRemoteEnabled[] = "devtools.remote_enabled";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 2ebb866..b40b4f0 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -570,6 +570,7 @@
 extern const char kDevToolsPortForwardingEnabled[];
 extern const char kDevToolsPortForwardingDefaultSet[];
 extern const char kDevToolsPortForwardingConfig[];
+extern const char kDevToolsPreferences[];
 #if defined(OS_ANDROID)
 extern const char kDevToolsRemoteEnabled[];
 #endif
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3997dc3..a3361dd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1673,7 +1673,7 @@
     }
   }
 
-  if (is_linux && !is_chromeos) {
+  if (is_win || (is_linux && !is_chromeos)) {
     # TODO(GYP): Figure out which of these work and are needed on other
     # platforms.
     test("chrome_app_unittests") {
@@ -1694,6 +1694,7 @@
 
       deps = [
         ":test_support",
+        "//breakpad:client",
         "//chrome/browser",
         "//chrome/child",
         "//base/test:run_all_unittests",
diff --git a/chrome/test/data/chromeos/wallpaper_manager/unit_tests/api_mock.js b/chrome/test/data/chromeos/wallpaper_manager/unit_tests/api_mock.js
index 143e6c33..c7d2b7be 100644
--- a/chrome/test/data/chromeos/wallpaper_manager/unit_tests/api_mock.js
+++ b/chrome/test/data/chromeos/wallpaper_manager/unit_tests/api_mock.js
@@ -201,6 +201,8 @@
       get: function(key, callback) {
         var items = {};
         switch (key) {
+          case Constants.AccessLocalSurpriseMeEnabledKey:
+            items[Constants.AccessLocalSurpriseMeEnabledKey] = true;
           case Constants.AccessLocalWallpaperInfoKey:
             items[Constants.AccessLocalWallpaperInfoKey] = {
               'url': 'dummy',
@@ -217,8 +219,8 @@
       get: function(key, callback) {
         var items = {};
         switch (key) {
-          case Constants.AccessSurpriseMeEnabledKey:
-            items[Constants.AccessSurpriseMeEnabledKey] = true;
+          case Constants.AccessSyncSurpriseMeEnabledKey:
+            items[Constants.AccessSyncSurpriseMeEnabledKey] = true;
           case Constants.AccessLastSurpriseWallpaperChangedDate:
             items[Constants.AccessLastSurpriseWallpaperChangedDate] =
                 new Date().toDateString();
@@ -266,7 +268,13 @@
                                  callback) {
     },
     getSyncSetting: function(callback) {
-      callback({syncThemes: true});
+      var setting = {};
+      setting.syncThemes = true;
+      callback(setting);
+    },
+    onWallpaperChangedBy3rdParty: {
+      addListener: function(listener) {
+      }
     }
   },
   runtime: {
diff --git a/chrome/test/data/chromeos/wallpaper_manager/unit_tests/event_page_unittest.js b/chrome/test/data/chromeos/wallpaper_manager/unit_tests/event_page_unittest.js
index 34f75cfb..01f7699 100644
--- a/chrome/test/data/chromeos/wallpaper_manager/unit_tests/event_page_unittest.js
+++ b/chrome/test/data/chromeos/wallpaper_manager/unit_tests/event_page_unittest.js
@@ -6,7 +6,9 @@
 var mockController;
 
 WallpaperUtil.enabledSyncThemesCallback = function(callback) {
-  callback();
+  chrome.wallpaperPrivate.getSyncSetting(function(setting) {
+    callback(setting.syncThemes);
+  });
 };
 
 function setUp() {
diff --git a/chrome/test/data/extensions/api_test/events_are_unregistered/manifest.json b/chrome/test/data/extensions/api_test/events_are_unregistered/manifest.json
new file mode 100644
index 0000000..ac97ea6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/events_are_unregistered/manifest.json
@@ -0,0 +1,7 @@
+{
+  "browser_action": {},
+  "description": "events_are_unregistered",
+  "manifest_version": 2,
+  "name": "events_are_unregistered",
+  "version": "1"
+}
diff --git a/chrome/test/data/extensions/api_test/events_are_unregistered/page1.html b/chrome/test/data/extensions/api_test/events_are_unregistered/page1.html
new file mode 100644
index 0000000..034c7de
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/events_are_unregistered/page1.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="page1.js"></script>
diff --git a/chrome/test/data/extensions/api_test/events_are_unregistered/page1.js b/chrome/test/data/extensions/api_test/events_are_unregistered/page1.js
new file mode 100644
index 0000000..c5ea62eb
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/events_are_unregistered/page1.js
@@ -0,0 +1,24 @@
+// 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.
+
+// Register for events in 4 configurations, then navigate to page2.html, which
+// will notify success and succeed the test on the C++ side. The C++ code
+// asserts that the events have been unregistered.
+
+// A single listener.
+chrome.browserAction.onClicked.addListener(function() {});
+// Multiple listeners for the same event.
+chrome.runtime.onStartup.addListener(function() {});
+chrome.runtime.onStartup.addListener(function() {});
+// A single listener, which previously had multiple listeners.
+chrome.runtime.onSuspend.addListener(function() {});
+chrome.runtime.onSuspend.addListener(function() {});
+chrome.runtime.onSuspend.removeListener(function() {});
+// No listeners, which previously had listeners (all were removed).
+chrome.runtime.onInstalled.addListener(function() {});
+chrome.runtime.onInstalled.addListener(function() {});
+chrome.runtime.onInstalled.removeListener(function() {});
+chrome.runtime.onInstalled.removeListener(function() {});
+
+location.assign('page2.html');
diff --git a/chrome/test/data/extensions/api_test/events_are_unregistered/page2.html b/chrome/test/data/extensions/api_test/events_are_unregistered/page2.html
new file mode 100644
index 0000000..aa09459b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/events_are_unregistered/page2.html
@@ -0,0 +1,6 @@
+<!--
+ * 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.
+-->
+<script src="page2.js"></script>
diff --git a/chrome/test/data/extensions/api_test/events_are_unregistered/page2.js b/chrome/test/data/extensions/api_test/events_are_unregistered/page2.js
new file mode 100644
index 0000000..26e65df
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/events_are_unregistered/page2.js
@@ -0,0 +1,6 @@
+// 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.
+
+// See page1.js to make sense of this.
+chrome.test.notifyPass();
diff --git a/chrome/test/data/webui/list_selection_model_test.html b/chrome/test/data/webui/list_selection_model_test.html
index f377f7d..58e7e70 100644
--- a/chrome/test/data/webui/list_selection_model_test.html
+++ b/chrome/test/data/webui/list_selection_model_test.html
@@ -195,6 +195,35 @@
   assertEquals(-1, sm.anchorIndex, 'anchor');
 }
 
+function testSelectAll() {
+  var sm = createSelectionModel(10);
+
+  var changes = null;
+  sm.addEventListener('change', function(e) {
+    changes = e.changes;
+  });
+
+  sm.selectAll();
+
+  assertArrayEquals(range(0, 9), sm.selectedIndexes);
+  assertArrayEquals(range(0, 9),
+                    changes.map(function(change) { return change.index; }));
+}
+
+function testSelectAllOnEmptyList() {
+  var sm = createSelectionModel(0);
+
+  var changes = null;
+  sm.addEventListener('change', function(e) {
+    changes = e.changes;
+  });
+
+  sm.selectAll();
+
+  assertArrayEquals([], sm.selectedIndexes);
+  assertEquals(null, changes);
+}
+
 </script>
 
 </body>
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index c145d9f..3234a59 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -17,6 +17,7 @@
 #include "chromecast/browser/cast_browser_main_parts.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_network_delegate.h"
+#include "chromecast/browser/cast_quota_permission_context.h"
 #include "chromecast/browser/cast_resource_dispatcher_host_delegate.h"
 #include "chromecast/browser/devtools/cast_dev_tools_delegate.h"
 #include "chromecast/browser/geolocation/cast_access_token_store.h"
@@ -197,6 +198,11 @@
   return locale.empty() ? "en-US" : locale;
 }
 
+content::QuotaPermissionContext*
+CastContentBrowserClient::CreateQuotaPermissionContext() {
+  return new CastQuotaPermissionContext();
+}
+
 void CastContentBrowserClient::AllowCertificateError(
     int render_process_id,
     int render_view_id,
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 83134fe..c35bc2ec 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -55,6 +55,7 @@
                            content::WebPreferences* prefs) override;
   void ResourceDispatcherHostCreated() override;
   std::string GetApplicationLocale() override;
+  content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
   void AllowCertificateError(
       int render_process_id,
       int render_view_id,
diff --git a/chromecast/browser/cast_quota_permission_context.cc b/chromecast/browser/cast_quota_permission_context.cc
new file mode 100644
index 0000000..e0ca804
--- /dev/null
+++ b/chromecast/browser/cast_quota_permission_context.cc
@@ -0,0 +1,23 @@
+// 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 "chromecast/browser/cast_quota_permission_context.h"
+
+namespace chromecast {
+
+CastQuotaPermissionContext::CastQuotaPermissionContext() {
+}
+
+CastQuotaPermissionContext::~CastQuotaPermissionContext() {
+}
+
+void CastQuotaPermissionContext::RequestQuotaPermission(
+    const content::StorageQuotaParams& params,
+    int render_process_id,
+    const PermissionCallback& callback) {
+  callback.Run(
+      content::QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_quota_permission_context.h b/chromecast/browser/cast_quota_permission_context.h
new file mode 100644
index 0000000..2dc41861
--- /dev/null
+++ b/chromecast/browser/cast_quota_permission_context.h
@@ -0,0 +1,30 @@
+// 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 CHROMECAST_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_
+#define CHROMECAST_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_
+
+#include "base/macros.h"
+#include "content/public/browser/quota_permission_context.h"
+
+namespace chromecast {
+
+class CastQuotaPermissionContext : public content::QuotaPermissionContext {
+ public:
+  CastQuotaPermissionContext();
+
+  // content::QuotaPermissionContext implementation:
+  void RequestQuotaPermission(const content::StorageQuotaParams& params,
+                              int render_process_id,
+                              const PermissionCallback& callback) override;
+
+ private:
+  ~CastQuotaPermissionContext() override;
+
+  DISALLOW_COPY_AND_ASSIGN(CastQuotaPermissionContext);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_QUOTA_PERMISSION_CONTEXT_H_
diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp
index fc4dbd52..82d885df 100644
--- a/chromecast/chromecast.gyp
+++ b/chromecast/chromecast.gyp
@@ -215,6 +215,8 @@
         'browser/cast_network_delegate.h',
         'browser/cast_permission_manager.cc',
         'browser/cast_permission_manager.h',
+        'browser/cast_quota_permission_context.cc',
+        'browser/cast_quota_permission_context.h',
         'browser/cast_resource_dispatcher_host_delegate.cc',
         'browser/cast_resource_dispatcher_host_delegate.h',
         'browser/devtools/cast_dev_tools_delegate.cc',
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 1cab62b..8e6ce9c 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-6969.0.0
\ No newline at end of file
+6972.0.0
\ No newline at end of file
diff --git a/components/OWNERS b/components/OWNERS
index ae1edd1..b69b99ad 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -77,10 +77,9 @@
 per-file error_page*=mmenke@chromium.org
 per-file error_page*=ttuttle@chromium.org
 
+per-file favicon*=pkotwicz@chromium.org
 per-file favicon*=sky@chromium.org
 per-file favicon*=stevenjb@chromium.org
-# Temporary for the duration of the favicon componentization.
-per-file favicon*=sdefresne@chromium.org
 
 per-file feedback.gypi=bsimonnet@chromium.org
 per-file feedback.gypi=zork@chromium.org
@@ -177,6 +176,7 @@
 per-file pdf.gypi=thestig@chromium.org
 
 per-file plugins.gypi=bauerb@chromium.org
+per-file plugins.gypi=tommycli@chromium.org
 
 per-file precache*=bengr@chromium.org
 per-file precache*=sclittle@chromium.org
diff --git a/components/autofill.gypi b/components/autofill.gypi
index b2209ea..fd5ceaf 100644
--- a/components/autofill.gypi
+++ b/components/autofill.gypi
@@ -43,6 +43,8 @@
         'autofill/core/common/autofill_constants.h',
         'autofill/core/common/autofill_data_validation.cc',
         'autofill/core/common/autofill_data_validation.h',
+        'autofill/core/common/autofill_l10n_util.cc',
+        'autofill/core/common/autofill_l10n_util.h',
         'autofill/core/common/autofill_pref_names.cc',
         'autofill/core/common/autofill_pref_names.h',
         'autofill/core/common/autofill_switches.cc',
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 021dfc5..1f711c7 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -263,12 +263,17 @@
   manager_->client()->HideAutofillPopup();
 }
 
-void AutofillExternalDelegate::RemoveSuggestion(const base::string16& value,
+bool AutofillExternalDelegate::RemoveSuggestion(const base::string16& value,
                                                 int identifier) {
   if (identifier > 0)
-    manager_->RemoveAutofillProfileOrCreditCard(identifier);
-  else
+    return manager_->RemoveAutofillProfileOrCreditCard(identifier);
+
+  if (identifier == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY) {
     manager_->RemoveAutocompleteEntry(query_field_.name, value);
+    return true;
+  }
+
+  return false;
 }
 
 void AutofillExternalDelegate::DidEndTextFieldEditing() {
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h
index 8e8c320..1f88ff0 100644
--- a/components/autofill/core/browser/autofill_external_delegate.h
+++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -42,7 +42,7 @@
                            int identifier) override;
   void DidAcceptSuggestion(const base::string16& value,
                            int identifier) override;
-  void RemoveSuggestion(const base::string16& value, int identifier) override;
+  bool RemoveSuggestion(const base::string16& value, int identifier) override;
   void ClearPreviewedForm() override;
 
   // Records and associates a query_id with web form data.  Called
diff --git a/components/autofill/core/browser/autofill_field.cc b/components/autofill/core/browser/autofill_field.cc
index 66417f5..e7e5732 100644
--- a/components/autofill/core/browser/autofill_field.cc
+++ b/components/autofill/core/browser/autofill_field.cc
@@ -17,6 +17,7 @@
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/phone_number.h"
 #include "components/autofill/core/browser/state_names.h"
+#include "components/autofill/core/common/autofill_l10n_util.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "grit/components_strings.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
@@ -380,27 +381,12 @@
   return base::UintToString(hash32);
 }
 
-scoped_ptr<icu::Collator> CreateCaseInsensitiveCollator() {
-  UErrorCode error = U_ZERO_ERROR;
-  scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
-  DCHECK(U_SUCCESS(error));
-  collator->setStrength(icu::Collator::PRIMARY);
-  return collator;
-}
-
 base::string16 RemoveWhitespace(const base::string16& value) {
   base::string16 stripped_value;
   base::RemoveChars(value, base::kWhitespaceUTF16, &stripped_value);
   return stripped_value;
 }
 
-bool StringsEqualWithCollator(const base::string16& lhs,
-                              const base::string16& rhs,
-                              const icu::Collator& collator) {
-  return base::i18n::CompareString16WithCollator(collator, lhs, rhs) ==
-      UCOL_EQUAL;
-}
-
 }  // namespace
 
 AutofillField::AutofillField()
@@ -548,21 +534,20 @@
 bool AutofillField::FindValueInSelectControl(const FormFieldData& field,
                                              const base::string16& value,
                                              size_t* index) {
-  scoped_ptr<icu::Collator> collator = CreateCaseInsensitiveCollator();
-
+  l10n::CaseInsensitiveCompare compare;
   // Strip off spaces for all values in the comparisons.
   const base::string16 value_stripped = RemoveWhitespace(value);
 
   for (size_t i = 0; i < field.option_values.size(); ++i) {
     base::string16 option_value = RemoveWhitespace(field.option_values[i]);
-    if (StringsEqualWithCollator(value_stripped, option_value, *collator)) {
+    if (compare.StringsEqual(value_stripped, option_value)) {
       if (index)
         *index = i;
       return true;
     }
 
     base::string16 option_contents = RemoveWhitespace(field.option_contents[i]);
-    if (StringsEqualWithCollator(value_stripped, option_contents, *collator)) {
+    if (compare.StringsEqual(value_stripped, option_contents)) {
       if (index)
         *index = i;
       return true;
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index b8ca3b1..8d26935 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -705,27 +705,34 @@
   client_->HideAutofillPopup();
 }
 
-void AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
+bool AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
   std::string guid;
   size_t variant = 0;
   const CreditCard* credit_card = nullptr;
   const AutofillProfile* profile = nullptr;
   if (GetCreditCard(unique_id, &credit_card)) {
+    if (credit_card->record_type() != CreditCard::LOCAL_CARD)
+      return false;
+
     guid = credit_card->guid();
   } else if (GetProfile(unique_id, &profile, &variant)) {
+    if (profile->record_type() != AutofillProfile::LOCAL_PROFILE)
+      return false;
+
     guid = profile->guid();
   } else {
     NOTREACHED();
-    return;
+    return false;
   }
 
   // TODO(csharp): If we are dealing with a variant only the variant should
   // be deleted, instead of doing nothing.
   // http://crbug.com/124211
   if (variant != 0)
-    return;
+    return false;
 
   personal_data_->RemoveByGUID(guid);
+  return true;
 }
 
 void AutofillManager::RemoveAutocompleteEntry(const base::string16& name,
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 7b4b9c4..542e8b8e 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -124,8 +124,8 @@
   void OnDidPreviewAutofillFormData();
 
   // Remove the credit card or Autofill profile that matches |unique_id|
-  // from the database.
-  void RemoveAutofillProfileOrCreditCard(int unique_id);
+  // from the database. Returns true if deletion is allowed.
+  bool RemoveAutofillProfileOrCreditCard(int unique_id);
 
   // Remove the specified Autocomplete entry.
   void RemoveAutocompleteEntry(const base::string16& name,
diff --git a/components/autofill/core/browser/autofill_popup_delegate.h b/components/autofill/core/browser/autofill_popup_delegate.h
index 97d60976..2eff7c11 100644
--- a/components/autofill/core/browser/autofill_popup_delegate.h
+++ b/components/autofill/core/browser/autofill_popup_delegate.h
@@ -28,8 +28,9 @@
   virtual void DidAcceptSuggestion(const base::string16& value,
                                    int identifier) = 0;
 
-  // Delete the described suggestion.
-  virtual void RemoveSuggestion(const base::string16& value,
+  // Delete the described suggestion. Returns true if something was deleted,
+  // or false if deletion is not allowed.
+  virtual bool RemoveSuggestion(const base::string16& value,
                                 int identifier) = 0;
 
   // Informs the delegate that the Autofill previewed form should be cleared.
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc
index 9ea27e62..5d582b3 100644
--- a/components/autofill/core/browser/autofill_profile.cc
+++ b/components/autofill/core/browser/autofill_profile.cc
@@ -28,6 +28,7 @@
 #include "components/autofill/core/browser/phone_number.h"
 #include "components/autofill/core/browser/phone_number_i18n.h"
 #include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/autofill_l10n_util.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "grit/components_strings.h"
 #include "third_party/icu/source/common/unicode/uchar.h"
@@ -237,21 +238,6 @@
   std::string app_locale_;
 };
 
-// Functor used to check for case-insensitive equality of two strings.
-struct CaseInsensitiveStringEquals {
- public:
-  CaseInsensitiveStringEquals(const base::string16& other)
-      : other_(other) {}
-
-  bool operator()(const base::string16& x) const {
-    return x.size() == other_.size() &&
-        base::StringToLowerASCII(x) == base::StringToLowerASCII(other_);
-  }
-
- private:
-  const base::string16& other_;
-};
-
 }  // namespace
 
 AutofillProfile::AutofillProfile(const std::string& guid,
@@ -577,6 +563,8 @@
     const AutofillProfile& profile,
     const std::string& app_locale,
     const ServerFieldTypeSet& types) const {
+  scoped_ptr<l10n::CaseInsensitiveCompare> compare;
+
   for (ServerFieldType type : types) {
     base::string16 value = GetRawInfo(type);
     if (value.empty())
@@ -599,9 +587,11 @@
                      app_locale)) {
         return false;
       }
-    } else if (base::StringToLowerASCII(value) !=
-               base::StringToLowerASCII(profile.GetRawInfo(type))) {
-      return false;
+    } else {
+      if (!compare)
+        compare.reset(new l10n::CaseInsensitiveCompare());
+      if (!compare->StringsEqual(value, profile.GetRawInfo(type)))
+        return false;
     }
   }
 
@@ -631,6 +621,7 @@
     const std::vector<NameInfo>& names,
     const std::string& app_locale) {
   std::vector<NameInfo> results(name_);
+  l10n::CaseInsensitiveCompare compare;
   for (std::vector<NameInfo>::const_iterator it = names.begin();
        it != names.end();
        ++it) {
@@ -651,8 +642,8 @@
 
       AutofillType type = AutofillType(NAME_FULL);
       base::string16 full_name = current_name.GetInfo(type, app_locale);
-      if (base::StringToLowerASCII(full_name) ==
-          base::StringToLowerASCII(imported_name.GetInfo(type, app_locale))) {
+      if (compare.StringsEqual(full_name,
+                               imported_name.GetInfo(type, app_locale))) {
         // The imported name has the same full name string as one of the
         // existing names for this profile.  Because full names are
         // _heuristically_ parsed into {first, middle, last} name components,
@@ -707,6 +698,8 @@
   // than full addresses.
   field_types.erase(ADDRESS_HOME_STREET_ADDRESS);
 
+  l10n::CaseInsensitiveCompare compare;
+
   for (ServerFieldTypeSet::const_iterator iter = field_types.begin();
        iter != field_types.end(); ++iter) {
     FieldTypeGroup group = AutofillType(*iter).group();
@@ -719,10 +712,8 @@
     // Single value field --- overwrite.
     if (!AutofillProfile::SupportsMultiValue(*iter)) {
       base::string16 new_value = profile.GetRawInfo(*iter);
-      if (base::StringToLowerASCII(GetRawInfo(*iter)) !=
-              base::StringToLowerASCII(new_value)) {
+      if (!compare.StringsEqual(GetRawInfo(*iter), new_value))
         SetRawInfo(*iter, new_value);
-      }
       continue;
     }
 
@@ -754,7 +745,9 @@
       } else {
         existing_iter =
             std::find_if(existing_values.begin(), existing_values.end(),
-                         CaseInsensitiveStringEquals(*value_iter));
+                         [&compare, value_iter](base::string16& rhs) {
+                           return compare.StringsEqual(*value_iter, rhs);
+                         });
       }
 
       if (existing_iter == existing_values.end())
diff --git a/components/autofill/core/browser/autofill_profile_unittest.cc b/components/autofill/core/browser/autofill_profile_unittest.cc
index 0303af1..cb5b647f 100644
--- a/components/autofill/core/browser/autofill_profile_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_unittest.cc
@@ -74,9 +74,9 @@
                       std::vector<base::string16>* middle_names,
                       std::vector<base::string16>* last_names) {
   for (size_t i = 0; i < names.size(); ++i) {
-    first_names->push_back(ASCIIToUTF16(names[i].first));
-    middle_names->push_back(ASCIIToUTF16(names[i].middle));
-    last_names->push_back(ASCIIToUTF16(names[i].last));
+    first_names->push_back(UTF8ToUTF16(names[i].first));
+    middle_names->push_back(UTF8ToUTF16(names[i].middle));
+    last_names->push_back(UTF8ToUTF16(names[i].last));
   }
 }
 
@@ -1301,6 +1301,10 @@
   test_cases.push_back(TestCase(NameParts("Marion", "Mitchell", "Morrison"),
                                 NameParts("MARION", "MITCHELL", "MORRISON"),
                                 NameParts("Marion", "Mitchell", "Morrison")));
+  // Capital A with acute versus lower case a with acute.
+  test_cases.push_back(TestCase(NameParts("M\xc3\xa1rion", "M", "Morrison"),
+                                NameParts("M\xc3\x81rion", "M", "Morrison"),
+                                NameParts("M\xc3\x81rion", "M", "Morrison")));
 
   // A parse that has a two-word last name should take precedence over a
   // parse that assumes the two names are a middle and a last name.
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index 85bad41..346d22b5 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/browser/autofill_regexes.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/autofill_l10n_util.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "grit/components_scaled_resources.h"
 #include "grit/components_strings.h"
@@ -70,8 +71,7 @@
 
   // Otherwise, try parsing the |month| as a named month, e.g. "January" or
   // "Jan".
-  base::string16 lowercased_month = base::StringToLowerASCII(month);
-
+  l10n::CaseInsensitiveCompare compare;
   UErrorCode status = U_ZERO_ERROR;
   icu::Locale locale(app_locale.c_str());
   icu::DateFormatSymbols date_format_symbols(locale, status);
@@ -81,9 +81,8 @@
   int32_t num_months;
   const icu::UnicodeString* months = date_format_symbols.getMonths(num_months);
   for (int32_t i = 0; i < num_months; ++i) {
-    const base::string16 icu_month = base::string16(months[i].getBuffer(),
-                                        months[i].length());
-    if (lowercased_month == base::StringToLowerASCII(icu_month)) {
+    const base::string16 icu_month(months[i].getBuffer(), months[i].length());
+    if (compare.StringsEqual(icu_month, month)) {
       *num = i + 1;  // Adjust from 0-indexed to 1-indexed.
       return true;
     }
@@ -91,9 +90,8 @@
 
   months = date_format_symbols.getShortMonths(num_months);
   for (int32_t i = 0; i < num_months; ++i) {
-    const base::string16 icu_month = base::string16(months[i].getBuffer(),
-                                        months[i].length());
-    if (lowercased_month == base::StringToLowerASCII(icu_month)) {
+    const base::string16 icu_month(months[i].getBuffer(), months[i].length());
+    if (compare.StringsEqual(icu_month, month)) {
       *num = i + 1;  // Adjust from 0-indexed to 1-indexed.
       return true;
     }
diff --git a/components/autofill/core/browser/credit_card_unittest.cc b/components/autofill/core/browser/credit_card_unittest.cc
index dde2b14..2bbadf4 100644
--- a/components/autofill/core/browser/credit_card_unittest.cc
+++ b/components/autofill/core/browser/credit_card_unittest.cc
@@ -446,6 +446,12 @@
       AutofillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("Apr"), "en-US");
   EXPECT_EQ(ASCIIToUTF16("04"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
   EXPECT_EQ(4, card.expiration_month());
+
+  card.SetInfo(
+      AutofillType(CREDIT_CARD_EXP_MONTH), UTF8ToUTF16("F\xc3\x89VRIER"),
+      "fr-FR");
+  EXPECT_EQ(ASCIIToUTF16("02"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+  EXPECT_EQ(2, card.expiration_month());
 }
 
 TEST(CreditCardTest, CreditCardType) {
diff --git a/components/autofill/core/common/BUILD.gn b/components/autofill/core/common/BUILD.gn
index e2f7e68..9ba33f3 100644
--- a/components/autofill/core/common/BUILD.gn
+++ b/components/autofill/core/common/BUILD.gn
@@ -8,6 +8,8 @@
     "autofill_constants.h",
     "autofill_data_validation.cc",
     "autofill_data_validation.h",
+    "autofill_l10n_util.cc",
+    "autofill_l10n_util.h",
     "autofill_pref_names.cc",
     "autofill_pref_names.h",
     "autofill_switches.cc",
diff --git a/components/autofill/core/common/autofill_l10n_util.cc b/components/autofill/core/common/autofill_l10n_util.cc
new file mode 100644
index 0000000..6f063a85
--- /dev/null
+++ b/components/autofill/core/common/autofill_l10n_util.cc
@@ -0,0 +1,30 @@
+// 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/autofill/core/common/autofill_l10n_util.h"
+
+#include "base/i18n/string_compare.h"
+#include "base/logging.h"
+
+namespace autofill {
+namespace l10n {
+
+CaseInsensitiveCompare::CaseInsensitiveCompare() {
+  UErrorCode error = U_ZERO_ERROR;
+  collator_.reset(icu::Collator::createInstance(error));
+  DCHECK(U_SUCCESS(error));
+  collator_->setStrength(icu::Collator::PRIMARY);
+}
+
+CaseInsensitiveCompare::~CaseInsensitiveCompare() {
+}
+
+bool CaseInsensitiveCompare::StringsEqual(const base::string16& lhs,
+                                          const base::string16& rhs) const {
+  return base::i18n::CompareString16WithCollator(*collator_, lhs, rhs) ==
+         UCOL_EQUAL;
+}
+
+}  // namespace l10n
+}  // namespace autofill
diff --git a/components/autofill/core/common/autofill_l10n_util.h b/components/autofill/core/common/autofill_l10n_util.h
new file mode 100644
index 0000000..099eb3af
--- /dev/null
+++ b/components/autofill/core/common/autofill_l10n_util.h
@@ -0,0 +1,27 @@
+// 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/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+
+namespace autofill {
+namespace l10n {
+
+// Assists with locale-aware case insensitive string comparisons.
+class CaseInsensitiveCompare {
+ public:
+  CaseInsensitiveCompare();
+  ~CaseInsensitiveCompare();
+
+  bool StringsEqual(const base::string16& lhs, const base::string16& rhs) const;
+
+ private:
+  scoped_ptr<icu::Collator> collator_;
+
+  DISALLOW_COPY_AND_ASSIGN(CaseInsensitiveCompare);
+};
+
+}  // namespace l10n
+}  // namespace autofill
diff --git a/components/components_browsertests.isolate b/components/components_browsertests.isolate
index f384f85f..2dff53ae 100644
--- a/components/components_browsertests.isolate
+++ b/components/components_browsertests.isolate
@@ -39,6 +39,7 @@
         'files': [
           'test/data/',
           '../testing/test_env.py',
+          '../third_party/dom_distiller_js/dist/test/data/',
           '<(PRODUCT_DIR)/components_browsertests<(EXECUTABLE_SUFFIX)',
           '<(PRODUCT_DIR)/components_tests_resources.pak',
           '<(PRODUCT_DIR)/content_shell.pak',
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index a1ab4f8..55029d9 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -403,6 +403,7 @@
       'proximity_auth/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc',
       'proximity_auth/cryptauth/cryptauth_api_call_flow_unittest.cc',
       'proximity_auth/cryptauth/cryptauth_client_impl_unittest.cc',
+      'proximity_auth/cryptauth/cryptauth_enroller_impl_unittest.cc',
       'proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc',
       'proximity_auth/proximity_auth_system_unittest.cc',
       'proximity_auth/remote_status_update_unittest.cc',
@@ -1122,6 +1123,7 @@
             'autofill/content/renderer/password_form_conversion_utils_browsertest.cc',
             'dom_distiller/content/distillable_page_utils_browsertest.cc',
             'dom_distiller/content/distiller_page_web_contents_browsertest.cc',
+            'dom_distiller/content/test/dom_distiller_js_browsertest.cc',
             'password_manager/content/renderer/credential_manager_client_browsertest.cc',
           ],
           'conditions': [
diff --git a/components/content_settings/core/browser/content_settings_default_provider.cc b/components/content_settings/core/browser/content_settings_default_provider.cc
index 4d3e08b6..0af5197 100644
--- a/components/content_settings/core/browser/content_settings_default_provider.cc
+++ b/components/content_settings/core/browser/content_settings_default_provider.cc
@@ -249,9 +249,14 @@
   scoped_ptr<base::Value> value(in_value);
   {
     base::AutoReset<bool> auto_reset(&updating_preferences_, true);
-    base::AutoLock lock(lock_);
-
-    ChangeSetting(content_type, value.get());
+    // Lock the memory map access, so that values are not read by
+    // |GetRuleIterator| at the same time as they are written here. Do not lock
+    // the preference access though; preference updates send out notifications
+    // whose callbacks may try to reacquire the lock on the same thread.
+    {
+      base::AutoLock lock(lock_);
+      ChangeSetting(content_type, value.get());
+    }
     WriteIndividualPref(content_type, value.get());
 
     // If the changed setting is syncable, write it to the old dictionary
@@ -374,19 +379,31 @@
     // If the dictionary preference gets synced from an old version
     // of Chrome, we should update all individual preferences that
     // are marked as syncable.
-    base::AutoLock lock(lock_);
     base::AutoReset<bool> auto_reset(&updating_preferences_, true);
 
     scoped_ptr<ValueMap> dictionary = ReadDictionaryPref();
 
+    // Lock the memory map access, so that values are not read by
+    // |GetRuleIterator| at the same time as they are written here. Do not lock
+    // the preference access though; preference updates send out notifications
+    // whose callbacks may try to reacquire the lock on the same thread.
+    {
+      base::AutoLock lock(lock_);
+      for (const auto& it : *dictionary) {
+        if (!IsContentSettingsTypeSyncable(it.first))
+          continue;
+
+        DCHECK(default_settings_.find(it.first) != default_settings_.end());
+        ChangeSetting(it.first, it.second.get());
+        to_notify.push_back(it.first);
+      }
+    }
+
+    // When the lock is released, write the new settings to preferences.
     for (const auto& it : *dictionary) {
       if (!IsContentSettingsTypeSyncable(it.first))
         continue;
-
-      DCHECK(default_settings_.find(it.first) != default_settings_.end());
-      ChangeSetting(it.first, it.second.get());
       WriteIndividualPref(it.first, it.second.get());
-      to_notify.push_back(it.first);
     }
   } else {
     // Find out which content setting the preference corresponds to.
@@ -407,10 +424,16 @@
     // A new individual preference is changed. If it is syncable, we should
     // change its entry in the dictionary preference as well, so that it
     // can be synced to older versions of Chrome.
-    base::AutoLock lock(lock_);
     base::AutoReset<bool> auto_reset(&updating_preferences_, true);
 
-    ChangeSetting(content_type, ReadIndividualPref(content_type).get());
+    // Lock the memory map access, so that values are not read by
+    // |GetRuleIterator| at the same time as they are written here. Do not lock
+    // the preference access though; preference updates send out notifications
+    // whose callbacks may try to reacquire the lock on the same thread.
+    {
+      base::AutoLock lock(lock_);
+      ChangeSetting(content_type, ReadIndividualPref(content_type).get());
+    }
     if (IsContentSettingsTypeSyncable(content_type))
       WriteDictionaryPref(content_type, default_settings_[content_type].get());
     to_notify.push_back(content_type);
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index 4c48da3..10357322 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -161,33 +161,24 @@
   if (!is_incognito_)
     map_to_modify = &value_map_;
 
-  std::vector<Rule> rules_to_delete;
   {
     base::AutoLock auto_lock(lock_);
-    scoped_ptr<RuleIterator> rule_iterator(
-        map_to_modify->GetRuleIterator(content_type_,
-            ResourceIdentifier(),
-            NULL));
-    // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
-    while (rule_iterator->HasNext())
-      rules_to_delete.push_back(rule_iterator->Next());
-
-    map_to_modify->DeleteValues(content_type_, ResourceIdentifier());
+    map_to_modify->clear();
   }
 
-  for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
-       it != rules_to_delete.end(); ++it) {
-    UpdatePref(it->primary_pattern,
-               it->secondary_pattern,
-               ResourceIdentifier(),
-               NULL);
-    if (IsContentSettingsTypeSyncable(content_type_)) {
-      UpdateOldPref(it->primary_pattern,
-                    it->secondary_pattern,
-                    ResourceIdentifier(),
-                    NULL);
+  if (!is_incognito_) {
+    // Clear the new preference.
+    {
+      base::AutoReset<bool> auto_reset(&updating_preferences_, true);
+      DictionaryPrefUpdate update(prefs_, pref_name_);
+      base::DictionaryValue* pattern_pairs_settings = update.Get();
+      pattern_pairs_settings->Clear();
     }
+
+    if (IsContentSettingsTypeSyncable(content_type_))
+      ClearOldPreference();
   }
+
   notify_callback_.Run(ContentSettingsPattern(),
                        ContentSettingsPattern(),
                        content_type_,
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
index 6c54949..75846c4 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
@@ -7,7 +7,7 @@
 import android.content.Context;
 import android.os.Build;
 
-import org.chromium.base.UsedByReflection;
+import org.chromium.base.annotations.UsedByReflection;
 
 import java.nio.channels.WritableByteChannel;
 import java.util.Map;
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
index a2d6446..78d3bee0 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
@@ -15,7 +15,7 @@
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.NativeClassQualifiedName;
-import org.chromium.base.UsedByReflection;
+import org.chromium.base.annotations.UsedByReflection;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
diff --git a/components/cronet/android/proguard.cfg b/components/cronet/android/proguard.cfg
index 3930cb94..65f4333 100644
--- a/components/cronet/android/proguard.cfg
+++ b/components/cronet/android/proguard.cfg
@@ -2,7 +2,7 @@
 -keep class org.chromium.base.*Native*
 -keep class org.chromium.base.JNINamespace
 -keepclasseswithmembers class org.chromium.** {
-    @org.chromium.base.AccessedByNative <fields>;
+    @org.chromium.base.annotations.AccessedByNative <fields>;
 }
 -keepclasseswithmembers class org.chromium.** {
     @org.chromium.base.*Native* <methods>;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
index 7022057..85400f36 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
@@ -32,7 +32,7 @@
 
     @SmallTest
     @Feature({"Cronet"})
-    public void disabled_testQuicLoadUrl() throws Exception {
+    public void testQuicLoadUrl() throws Exception {
         HttpUrlRequestFactoryConfig config = new HttpUrlRequestFactoryConfig();
         String quicURL = QuicTestServer.getServerURL() + "/simple.txt";
         config.enableQUIC(true);
@@ -47,9 +47,8 @@
         HashMap<String, String> headers = new HashMap<String, String>();
         TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener();
 
-        // Quic always races the first request with SPDY. Since our test server
-        // only supports Quic, request will either succeed with a 200 or fail
-        // with a 500.
+        // Quic always races the first request with SPDY, but the second request
+        // should go through Quic.
         for (int i = 0; i < 2; i++) {
             HttpUrlRequest request =
                     factory.createRequest(
@@ -59,8 +58,6 @@
                             listener);
             request.start();
             listener.blockForComplete();
-            assertTrue(listener.mHttpStatusCode == 200
-                    || listener.mHttpStatusCode == 500);
             if (listener.mHttpStatusCode == 200) break;
         }
         assertEquals(200, listener.mHttpStatusCode);
diff --git a/components/cronet/android/test/quic_test_server.cc b/components/cronet/android/test/quic_test_server.cc
index 04dfd549..ff0b859e 100644
--- a/components/cronet/android/test/quic_test_server.cc
+++ b/components/cronet/android/test/quic_test_server.cc
@@ -25,19 +25,23 @@
 base::Thread* g_quic_server_thread = nullptr;
 net::tools::QuicSimpleServer* g_quic_server = nullptr;
 
-void ServeFilesFromDirectory(const base::FilePath& directory) {
+void ServeFilesFromDirectory(
+    const base::FilePath& directory,
+    base::android::ScopedJavaGlobalRef<jobject>* callback) {
   DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
   DCHECK(!g_quic_server);
   base::FilePath file_dir = directory.Append("quic_data");
   net::tools::QuicInMemoryCache::GetInstance()->InitializeFromDirectory(
       file_dir.value());
   net::IPAddressNumber ip;
-  DCHECK(net::ParseIPLiteralToNumber(kServerHost, &ip));
+  net::ParseIPLiteralToNumber(kServerHost, &ip);
   net::QuicConfig config;
   g_quic_server =
       new net::tools::QuicSimpleServer(config, net::QuicSupportedVersions());
-  DCHECK(g_quic_server->Listen(net::IPEndPoint(ip, kServerPort)) >= 0) <<
-      "Quic server fails to start";
+  int rv = g_quic_server->Listen(net::IPEndPoint(ip, kServerPort));
+  CHECK_GE(rv, 0) << "Quic server fails to start";
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_QuicTestServer_onServerStarted(env, callback->obj());
 }
 
 void ShutdownOnServerThread() {
@@ -57,11 +61,16 @@
   g_quic_server_thread = new base::Thread("quic server thread");
   base::Thread::Options thread_options;
   thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
-  DCHECK(g_quic_server_thread->StartWithOptions(thread_options));
+  bool started = g_quic_server_thread->StartWithOptions(thread_options);
+  DCHECK(started);
   base::FilePath test_files_root(
       base::android::ConvertJavaStringToUTF8(env, jtest_files_root));
+  base::android::ScopedJavaGlobalRef<jobject>* callback =
+      new base::android::ScopedJavaGlobalRef<jobject>();
+  callback->Reset(env, jcaller);
   g_quic_server_thread->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&ServeFilesFromDirectory, test_files_root));
+      FROM_HERE, base::Bind(&ServeFilesFromDirectory, test_files_root,
+                            base::Owned(callback)));
 }
 
 void ShutdownQuicTestServer(JNIEnv* env, jclass jcaller) {
diff --git a/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java b/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java
index b299156..78fbfde7 100644
--- a/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java
+++ b/components/cronet/android/test/src/org/chromium/net/QuicTestServer.java
@@ -5,7 +5,9 @@
 package org.chromium.net;
 
 import android.content.Context;
+import android.os.ConditionVariable;
 
+import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 
 /**
@@ -13,12 +15,16 @@
  */
 @JNINamespace("cronet")
 public final class QuicTestServer {
+    private static final ConditionVariable sBlock = new ConditionVariable();
+
     public static void startQuicTestServer(Context context) {
         nativeStartQuicTestServer(TestFilesInstaller.getInstalledPath(context));
+        sBlock.block();
     }
 
     public static void shutdownQuicTestServer() {
         nativeShutdownQuicTestServer();
+        sBlock.close();
     }
 
     public static String getServerURL() {
@@ -33,6 +39,11 @@
         return nativeGetServerPort();
     }
 
+    @CalledByNative
+    private void onServerStarted() {
+        sBlock.open();
+    }
+
     private static native void nativeStartQuicTestServer(String filePath);
     private static native void nativeShutdownQuicTestServer();
     private static native String nativeGetServerHost();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 59081c3..f8e3d9e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -12,7 +12,6 @@
 #include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
@@ -86,22 +85,17 @@
     const net::BackoffEntry::Policy& backoff_policy,
     DataReductionProxyRequestOptions* request_options,
     DataReductionProxyMutableConfigValues* config_values,
-    DataReductionProxyConfig* config,
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    DataReductionProxyConfig* config)
     : params_(params.Pass()),
       request_options_(request_options),
       config_values_(config_values),
       config_(config),
-      io_task_runner_(io_task_runner),
       backoff_entry_(&backoff_policy) {
   DCHECK(request_options);
   DCHECK(config_values);
   DCHECK(config);
-  DCHECK(io_task_runner.get());
-  io_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&DataReductionProxyConfigServiceClient::RetrieveConfig,
-                 base::Unretained(this)));
+  // Constructed on the UI thread, but should be checked on the IO thread.
+  thread_checker_.DetachFromThread();
 }
 
 DataReductionProxyConfigServiceClient::
@@ -109,8 +103,7 @@
 }
 
 void DataReductionProxyConfigServiceClient::RetrieveConfig() {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
-
+  DCHECK(thread_checker_.CalledOnValidThread());
   std::string static_response = ConstructStaticResponse();
   ClientConfig config;
   bool succeeded = false;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
index 3f6ae23..e51ea83 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
@@ -9,13 +9,12 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
 #include "net/base/backoff_entry.h"
 
 namespace base {
-class SingleThreadTaskRunner;
 class Time;
 class TimeDelta;
 }
@@ -43,8 +42,7 @@
       const net::BackoffEntry::Policy& backoff_policy,
       DataReductionProxyRequestOptions* request_options,
       DataReductionProxyMutableConfigValues* config_values,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+      DataReductionProxyConfig* config);
 
   virtual ~DataReductionProxyConfigServiceClient();
 
@@ -86,10 +84,6 @@
   // The caller must ensure that the |config_| outlives this instance.
   DataReductionProxyConfig* config_;
 
-  // |io_task_runner_| should be the task runner for running operations on the
-  // IO thread.
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-
   // Used to calculate the backoff time on request failures.
   net::BackoffEntry backoff_entry_;
 
@@ -98,6 +92,9 @@
   base::OneShotTimer<DataReductionProxyConfigServiceClient>
       config_refresh_timer_;
 
+  // Enforce usage on the IO thread.
+  base::ThreadChecker thread_checker_;
+
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfigServiceClient);
 };
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index f8e7c0f3..b72ee5746 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -75,16 +75,13 @@
         DataReductionProxyParams::kAllowed |
         DataReductionProxyParams::kFallbackAllowed |
         DataReductionProxyParams::kPromoAllowed));
-    request_options_.reset(
-        new DataReductionProxyRequestOptions(Client::UNKNOWN,
-                                             test_context_->io_data()->config(),
-                                             test_context_->task_runner()));
+    request_options_.reset(new DataReductionProxyRequestOptions(
+        Client::UNKNOWN, test_context_->io_data()->config()));
     return scoped_ptr<DataReductionProxyConfigServiceClient>(
         new DataReductionProxyConfigServiceClient(
-            params.Pass(), GetBackoffPolicy(),
-            request_options_.get(),
+            params.Pass(), GetBackoffPolicy(), request_options_.get(),
             test_context_->mutable_config_values(),
-            test_context_->io_data()->config(), test_context_->task_runner()));
+            test_context_->io_data()->config()));
   }
 
   DataReductionProxyParams* params() {
@@ -133,7 +130,7 @@
       .WillRepeatedly(
           testing::Invoke(&populator,
                           &RequestOptionsPopulator::PopulateResponse));
-  RunUntilIdle();
+  config_client()->RetrieveConfig();
   EXPECT_EQ(base::TimeDelta::FromDays(1), config_client()->GetDelay());
   EXPECT_EQ(params()->origin().ToURI(), configurator()->origin());
   EXPECT_EQ(params()->fallback_origin().ToURI(),
@@ -162,7 +159,7 @@
       .Times(1)
       .WillOnce(testing::Invoke(&populator,
                                 &RequestOptionsPopulator::PopulateResponse));
-  RunUntilIdle();
+  config_client()->RetrieveConfig();
   EXPECT_EQ(base::TimeDelta::FromSeconds(10), config_client()->GetDelay());
   EXPECT_EQ(params()->origin().ToURI(), configurator()->origin());
   EXPECT_EQ(params()->fallback_origin().ToURI(),
@@ -202,7 +199,7 @@
       .Times(1)
       .WillOnce(testing::Invoke(&populator,
                                 &RequestOptionsPopulator::PopulateResponse));
-  RunUntilIdle();
+  config_client()->RetrieveConfig();
   EXPECT_TRUE(configurator()->origin().empty());
   EXPECT_TRUE(configurator()->fallback_origin().empty());
   EXPECT_TRUE(configurator()->ssl_origin().empty());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
index 4009c21..2a804f1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.cc
@@ -4,7 +4,6 @@
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 
-#include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
@@ -13,15 +12,13 @@
 namespace data_reduction_proxy {
 
 DataReductionProxyConfigurator::DataReductionProxyConfigurator(
-    scoped_refptr<base::SequencedTaskRunner> network_task_runner,
     net::NetLog* net_log,
     DataReductionProxyEventStore* event_store)
-    : network_task_runner_(network_task_runner),
-      net_log_(net_log),
-      data_reduction_proxy_event_store_(event_store) {
-  DCHECK(network_task_runner.get());
+    : net_log_(net_log), data_reduction_proxy_event_store_(event_store) {
   DCHECK(net_log);
   DCHECK(event_store);
+  // Constructed on the UI thread, but should be checked on the IO thread.
+  thread_checker_.DetachFromThread();
 }
 
 DataReductionProxyConfigurator::~DataReductionProxyConfigurator() {
@@ -33,6 +30,7 @@
     const std::string& primary_origin,
     const std::string& fallback_origin,
     const std::string& ssl_origin) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   std::vector<std::string> proxies;
   if (!primary_restricted) {
     std::string trimmed_primary;
@@ -68,32 +66,25 @@
   data_reduction_proxy_event_store_->AddProxyEnabledEvent(
       net_log_, primary_restricted, fallback_restricted, primary_origin,
       fallback_origin, ssl_origin);
-  network_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(
-          &DataReductionProxyConfigurator::UpdateProxyConfigOnIOThread,
-          base::Unretained(this),
-          config));
+  config_ = config;
 }
 
 void DataReductionProxyConfigurator::Disable() {
+  DCHECK(thread_checker_.CalledOnValidThread());
   net::ProxyConfig config = net::ProxyConfig::CreateDirect();
   data_reduction_proxy_event_store_->AddProxyDisabledEvent(net_log_);
-  network_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(
-          &DataReductionProxyConfigurator::UpdateProxyConfigOnIOThread,
-          base::Unretained(this),
-          config));
+  config_ = config;
 }
 
 void DataReductionProxyConfigurator::AddHostPatternToBypass(
     const std::string& pattern) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   bypass_rules_.push_back(pattern);
 }
 
 void DataReductionProxyConfigurator::AddURLPatternToBypass(
     const std::string& pattern) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   size_t pos = pattern.find('/');
   if (pattern.find('/', pos + 1) == pos + 1)
     pos = pattern.find('/', pos + 2);
@@ -107,13 +98,8 @@
   AddHostPatternToBypass(host_pattern);
 }
 
-void DataReductionProxyConfigurator::UpdateProxyConfigOnIOThread(
-    const net::ProxyConfig& config) {
-  config_ = config;
-}
-
-const net::ProxyConfig&
-DataReductionProxyConfigurator::GetProxyConfigOnIOThread() const {
+const net::ProxyConfig& DataReductionProxyConfigurator::GetProxyConfig() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
   return config_;
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
index 82b1d09..e239b542 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h
@@ -9,13 +9,9 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "base/task_runner.h"
+#include "base/threading/thread_checker.h"
 #include "net/proxy/proxy_config.h"
 
-namespace base {
-class SequencedTaskRunner;
-}
-
 namespace net {
 class NetLog;
 class ProxyInfo;
@@ -30,23 +26,15 @@
 
 class DataReductionProxyConfigurator {
  public:
-  // Constructs a configurator. |network_task_runner| should be the task runner
-  // for running network operations, |net_log| and |event_store| are used to
+  // Constructs a configurator. |net_log| and |event_store| are used to
   // track network and Data Reduction Proxy events respectively, must not be
   // null, and must outlive this instance.
   DataReductionProxyConfigurator(
-      scoped_refptr<base::SequencedTaskRunner> network_task_runner,
       net::NetLog* net_log,
       DataReductionProxyEventStore* event_store);
 
   virtual ~DataReductionProxyConfigurator();
 
-  void set_net_log(net::NetLog* net_log) {
-    DCHECK(!net_log_);
-    net_log_ = net_log;
-    DCHECK(net_log_);
-  }
-
   // Constructs a proxy configuration suitable for enabling the Data Reduction
   // proxy.
   virtual void Enable(bool primary_restricted,
@@ -71,19 +59,13 @@
   // as a hostname pattern.
   virtual void AddURLPatternToBypass(const std::string& pattern);
 
-  // Updates the config for use on the IO thread.
-  void UpdateProxyConfigOnIOThread(const net::ProxyConfig& config);
-
   // Returns the current data reduction proxy config, even if it is not the
   // effective configuration used by the proxy service.
-  const net::ProxyConfig& GetProxyConfigOnIOThread() const;
+  const net::ProxyConfig& GetProxyConfig() const;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfiguratorTest, TestBypassList);
 
-  // Used for updating the proxy config on the IO thread.
-  scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
-
   // Rules for bypassing the Data Reduction Proxy.
   std::vector<std::string> bypass_rules_;
 
@@ -96,6 +78,9 @@
   net::NetLog* net_log_;
   DataReductionProxyEventStore* data_reduction_proxy_event_store_;
 
+  // Enforce usage on the IO thread.
+  base::ThreadChecker thread_checker_;
+
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfigurator);
 };
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc
index a86845f..e373aad 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.cc
@@ -7,10 +7,9 @@
 namespace data_reduction_proxy {
 
 TestDataReductionProxyConfigurator::TestDataReductionProxyConfigurator(
-    scoped_refptr<base::SequencedTaskRunner> network_task_runner,
     net::NetLog* net_log,
     DataReductionProxyEventStore* event_store)
-    : DataReductionProxyConfigurator(network_task_runner, net_log, event_store),
+    : DataReductionProxyConfigurator(net_log, event_store),
       enabled_(false),
       restricted_(false),
       fallback_restricted_(false) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h
index 5b0a82c9..bcfccd5 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h
@@ -7,8 +7,6 @@
 
 #include <string>
 
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 
 namespace net {
@@ -23,7 +21,6 @@
     : public DataReductionProxyConfigurator {
  public:
   TestDataReductionProxyConfigurator(
-      scoped_refptr<base::SequencedTaskRunner> network_task_runner,
       net::NetLog* net_log,
       DataReductionProxyEventStore* event_store);
   ~TestDataReductionProxyConfigurator() override;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
index b821a25..bb8fb3d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
@@ -23,10 +23,8 @@
     net_log_.reset(new net::CapturingNetLog());
     data_reduction_proxy_event_store_.reset(
         new data_reduction_proxy::DataReductionProxyEventStore(task_runner_));
-    config_.reset(
-        new DataReductionProxyConfigurator(
-            task_runner_, net_log_.get(),
-            data_reduction_proxy_event_store_.get()));
+    config_.reset(new DataReductionProxyConfigurator(
+        net_log_.get(), data_reduction_proxy_event_store_.get()));
   }
 
   void CheckProxyConfig(
@@ -36,7 +34,7 @@
       const std::string& expected_bypass_list) {
     task_runner_->RunUntilIdle();
     const net::ProxyConfig::ProxyRules& rules =
-        config_->GetProxyConfigOnIOThread().proxy_rules();
+        config_->GetProxyConfig().proxy_rules();
     ASSERT_EQ(expected_rules_type, rules.type);
     if (net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME ==
         expected_rules_type) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
index c257d67..8475211 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
@@ -302,7 +302,7 @@
 
     // Three proxies should be available for use: primary, fallback, and direct.
     const net::ProxyConfig& proxy_config =
-        drp_test_context_->configurator()->GetProxyConfigOnIOThread();
+        drp_test_context_->configurator()->GetProxyConfig();
     EXPECT_EQ(3U, proxy_config.proxy_rules().proxies_for_http.size());
   }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 8fd3ef51..d025624f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -48,14 +48,13 @@
       new DataReductionProxyParams(param_flags));
   params->EnableQuic(enable_quic);
   event_store_.reset(new DataReductionProxyEventStore(ui_task_runner));
-  configurator_.reset(new DataReductionProxyConfigurator(
-      io_task_runner, net_log, event_store_.get()));
+  configurator_.reset(
+      new DataReductionProxyConfigurator(net_log, event_store_.get()));
   bool use_config_client = DataReductionProxyParams::IsConfigClientEnabled();
   DataReductionProxyMutableConfigValues* raw_mutable_config = nullptr;
   if (use_config_client) {
     scoped_ptr<DataReductionProxyMutableConfigValues> mutable_config =
-        DataReductionProxyMutableConfigValues::CreateFromParams(io_task_runner_,
-                                                                params.get());
+        DataReductionProxyMutableConfigValues::CreateFromParams(params.get());
     raw_mutable_config = mutable_config.get();
     config_.reset(new DataReductionProxyConfig(
         io_task_runner_, ui_task_runner_, net_log, mutable_config.Pass(),
@@ -72,13 +71,13 @@
   bypass_stats_.reset(new DataReductionProxyBypassStats(
       config_.get(), base::Bind(&DataReductionProxyIOData::SetUnreachable,
                                 base::Unretained(this))));
-  request_options_.reset(new DataReductionProxyRequestOptions(
-      client_, config_.get(), io_task_runner_));
+  request_options_.reset(
+      new DataReductionProxyRequestOptions(client_, config_.get()));
   request_options_->Init();
   if (use_config_client) {
     config_client_.reset(new DataReductionProxyConfigServiceClient(
         params.Pass(), GetBackoffPolicy(), request_options_.get(),
-        raw_mutable_config, config_.get(), io_task_runner_));
+        raw_mutable_config, config_.get()));
   }
 
   proxy_delegate_.reset(
@@ -122,6 +121,8 @@
 
 void DataReductionProxyIOData::InitializeOnIOThread() {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (config_client_.get())
+    config_client_->RetrieveConfig();
   ui_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(&DataReductionProxyService::SetIOData,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
index cebbdaca..19f935e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
@@ -4,17 +4,15 @@
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h"
 
-#include "base/single_thread_task_runner.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 
 namespace data_reduction_proxy {
 
 scoped_ptr<DataReductionProxyMutableConfigValues>
 DataReductionProxyMutableConfigValues::CreateFromParams(
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
     const DataReductionProxyParams* params) {
   scoped_ptr<DataReductionProxyMutableConfigValues> config_values(
-      new DataReductionProxyMutableConfigValues(io_task_runner));
+      new DataReductionProxyMutableConfigValues());
   config_values->promo_allowed_ = params->promo_allowed();
   config_values->holdback_ = params->holdback();
   config_values->allowed_ = params->allowed();
@@ -23,17 +21,16 @@
   return config_values.Pass();
 }
 
-DataReductionProxyMutableConfigValues::DataReductionProxyMutableConfigValues(
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+DataReductionProxyMutableConfigValues::DataReductionProxyMutableConfigValues()
     : empty_origin_(),
       promo_allowed_(false),
       holdback_(false),
       allowed_(false),
       fallback_allowed_(false),
       origin_(empty_origin_),
-      fallback_origin_(empty_origin_),
-      io_task_runner_(io_task_runner) {
-  DCHECK(io_task_runner.get());
+      fallback_origin_(empty_origin_) {
+  // Constructed on the UI thread, but should be checked on the IO thread.
+  thread_checker_.DetachFromThread();
 }
 
 DataReductionProxyMutableConfigValues::
@@ -99,13 +96,13 @@
 }
 
 const net::ProxyServer& DataReductionProxyMutableConfigValues::origin() const {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   return origin_;
 }
 
 const net::ProxyServer& DataReductionProxyMutableConfigValues::fallback_origin()
     const {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   return fallback_origin_;
 }
 
@@ -132,7 +129,7 @@
 void DataReductionProxyMutableConfigValues::UpdateValues(
     const net::ProxyServer& origin,
     const net::ProxyServer& fallback_origin) {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   origin_ = origin;
   fallback_origin_ = fallback_origin;
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
index 6897cb9..264ef17 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
@@ -6,16 +6,12 @@
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_MUTABLE_CONFIG_VALUES_H_
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h"
 #include "net/proxy/proxy_server.h"
 #include "url/gurl.h"
 
-namespace base {
-class SingleThreadTaskRunner;
-}
-
 namespace data_reduction_proxy {
 
 class DataReductionProxyParams;
@@ -28,7 +24,6 @@
   // Creates a new |DataReductionProxyMutableConfigValues| using |params| as
   // the basis for its initial values.
   static scoped_ptr<DataReductionProxyMutableConfigValues> CreateFromParams(
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
       const DataReductionProxyParams* params);
 
   ~DataReductionProxyMutableConfigValues() override;
@@ -57,8 +52,7 @@
   const GURL& secure_proxy_check_url() const override;
 
  protected:
-  DataReductionProxyMutableConfigValues(
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+  DataReductionProxyMutableConfigValues();
 
  private:
   net::ProxyServer empty_origin_;
@@ -70,9 +64,8 @@
   net::ProxyServer fallback_origin_;
   GURL secure_proxy_check_url_;
 
-  // |io_task_runner_| should be the task runner for running operations on the
-  // IO thread.
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  // Enforce usage on the IO thread.
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyMutableConfigValues);
 };
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index c9b9cb1b..6ef70dc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -81,10 +81,11 @@
     : LayeredNetworkDelegate(network_delegate.Pass()),
       received_content_length_(0),
       original_content_length_(0),
-      data_reduction_proxy_enabled_(NULL),
+      data_reduction_proxy_enabled_(nullptr),
       data_reduction_proxy_config_(config),
-      data_reduction_proxy_bypass_stats_(NULL),
+      data_reduction_proxy_bypass_stats_(nullptr),
       data_reduction_proxy_request_options_(request_options),
+      data_reduction_proxy_io_data_(nullptr),
       configurator_(configurator) {
   DCHECK(data_reduction_proxy_config_);
   DCHECK(data_reduction_proxy_request_options_);
@@ -121,9 +122,9 @@
     const net::ProxyService& proxy_service,
     net::ProxyInfo* result) {
   if (configurator_) {
-    OnResolveProxyHandler(
-        url, load_flags, configurator_->GetProxyConfigOnIOThread(),
-        proxy_service.proxy_retry_info(), data_reduction_proxy_config_, result);
+    OnResolveProxyHandler(url, load_flags, configurator_->GetProxyConfig(),
+                          proxy_service.proxy_retry_info(),
+                          data_reduction_proxy_config_, result);
   }
 }
 
@@ -175,10 +176,9 @@
         request->response_info().headers->GetFreshnessLifetimes(
             request->response_info().response_time).freshness;
     DataReductionProxyRequestType request_type =
-        GetDataReductionProxyRequestType(
-            *request,
-            configurator_->GetProxyConfigOnIOThread(),
-            *data_reduction_proxy_config_);
+        GetDataReductionProxyRequestType(*request,
+                                         configurator_->GetProxyConfig(),
+                                         *data_reduction_proxy_config_);
 
     int64 adjusted_original_content_length =
         GetAdjustedOriginalContentLength(request_type,
@@ -194,9 +194,8 @@
     if (data_reduction_proxy_enabled_ &&
         data_reduction_proxy_bypass_stats_) {
       data_reduction_proxy_bypass_stats_->RecordBytesHistograms(
-          *request,
-          *data_reduction_proxy_enabled_,
-          configurator_->GetProxyConfigOnIOThread());
+          *request, *data_reduction_proxy_enabled_,
+          configurator_->GetProxyConfig());
     }
     DVLOG(2) << __FUNCTION__
         << " received content length: " << received_content_length
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 1b95890b..8bd3d23 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -96,25 +96,25 @@
 
 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
     Client client,
-    DataReductionProxyConfig* config,
-    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
+    DataReductionProxyConfig* config)
     : client_(GetString(client)),
       use_assigned_credentials_(false),
-      data_reduction_proxy_config_(config),
-      network_task_runner_(network_task_runner) {
+      data_reduction_proxy_config_(config) {
   GetChromiumBuildAndPatch(ChromiumVersion(), &build_, &patch_);
+  // Constructed on the UI thread, but should be checked on the IO thread.
+  thread_checker_.DetachFromThread();
 }
 
 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
     Client client,
     const std::string& version,
-    DataReductionProxyConfig* config,
-    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
+    DataReductionProxyConfig* config)
     : client_(GetString(client)),
       use_assigned_credentials_(false),
-      data_reduction_proxy_config_(config),
-      network_task_runner_(network_task_runner) {
+      data_reduction_proxy_config_(config) {
   GetChromiumBuildAndPatch(version, &build_, &patch_);
+  // Constructed on the UI thread, but should be checked on the IO thread.
+  thread_checker_.DetachFromThread();
 }
 
 DataReductionProxyRequestOptions::~DataReductionProxyRequestOptions() {
@@ -208,7 +208,7 @@
     net::URLRequest* request,
     const net::ProxyServer& proxy_server,
     net::HttpRequestHeaders* request_headers) {
-  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   if (!proxy_server.is_valid())
     return;
   if (proxy_server.is_direct())
@@ -221,7 +221,7 @@
 void DataReductionProxyRequestOptions::MaybeAddProxyTunnelRequestHandler(
     const net::HostPortPair& proxy_server,
     net::HttpRequestHeaders* request_headers) {
-  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   MaybeAddRequestHeaderImpl(proxy_server, true, request_headers);
 }
 
@@ -273,6 +273,7 @@
 }
 
 void DataReductionProxyRequestOptions::SetKeyOnIO(const std::string& key) {
+  DCHECK(thread_checker_.CalledOnValidThread());
   if(!key.empty()) {
     key_ = key;
     UpdateCredentials();
@@ -281,7 +282,7 @@
 
 void DataReductionProxyRequestOptions::PopulateConfigResponse(
     base::DictionaryValue* response) const {
-  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   std::string session;
   std::string credentials;
   base::Time now = Now();
@@ -296,7 +297,7 @@
 void DataReductionProxyRequestOptions::SetCredentials(
     const std::string& session,
     const std::string& credentials) {
-  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
   session_ = session;
   credentials_ = credentials;
   // Force skipping of credential regeneration. It should be handled by the
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index 8d6c289..2aa8dce 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -9,13 +9,12 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 
 namespace base {
 class DictionaryValue;
-class SingleThreadTaskRunner;
 }
 
 namespace net {
@@ -83,11 +82,9 @@
                                    std::string* credentials);
 
   // Constructs a DataReductionProxyRequestOptions object with the given
-  // client type, config, and network task runner.
-  DataReductionProxyRequestOptions(
-      Client client,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
+  // client type, and config.
+  DataReductionProxyRequestOptions(Client client,
+                                   DataReductionProxyConfig* config);
 
   virtual ~DataReductionProxyRequestOptions();
 
@@ -145,11 +142,9 @@
   virtual std::string GetDefaultKey() const;
 
   // Visible for testing.
-  DataReductionProxyRequestOptions(
-      Client client,
-      const std::string& version,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
+  DataReductionProxyRequestOptions(Client client,
+                                   const std::string& version,
+                                   DataReductionProxyConfig* config);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
@@ -219,9 +214,11 @@
   // |SetCredentials|.
   bool use_assigned_credentials_;
 
+  // Must outlive |this|.
   DataReductionProxyConfig* data_reduction_proxy_config_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+  // Enforce usage on the IO thread.
+  base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyRequestOptions);
 };
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index d34ebf2..95c3465 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -135,10 +135,8 @@
   }
 
   void CreateRequestOptions(const std::string& version) {
-    request_options_.reset(
-        new TestDataReductionProxyRequestOptions(
-            kClient, version, test_context_->config(),
-            test_context_->task_runner()));
+    request_options_.reset(new TestDataReductionProxyRequestOptions(
+        kClient, version, test_context_->config()));
     request_options_->Init();
   }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index 91a71cb9..7d04b1e7 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -13,7 +13,6 @@
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
index 5c6eef6..bfe3947 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
@@ -23,7 +23,6 @@
 
 namespace data_reduction_proxy {
 
-class DataReductionProxyConfigurator;
 class DataReductionProxyTestContext;
 class MockDataReductionProxyConfig;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index f479053a..921a99a 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -45,9 +45,8 @@
 TestDataReductionProxyRequestOptions::TestDataReductionProxyRequestOptions(
     Client client,
     const std::string& version,
-    DataReductionProxyConfig* config,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : DataReductionProxyRequestOptions(client, version, config, task_runner) {
+    DataReductionProxyConfig* config)
+    : DataReductionProxyRequestOptions(client, version, config) {
 }
 
 std::string TestDataReductionProxyRequestOptions::GetDefaultKey() const {
@@ -75,9 +74,8 @@
 MockDataReductionProxyRequestOptions::MockDataReductionProxyRequestOptions(
     Client client,
     const std::string& version,
-    DataReductionProxyConfig* config,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : DataReductionProxyRequestOptions(client, version, config, task_runner) {
+    DataReductionProxyConfig* config)
+    : DataReductionProxyRequestOptions(client, version, config) {
 }
 
 MockDataReductionProxyRequestOptions::~MockDataReductionProxyRequestOptions() {
@@ -88,14 +86,12 @@
         scoped_ptr<DataReductionProxyParams> params,
         DataReductionProxyRequestOptions* request_options,
         DataReductionProxyMutableConfigValues* config_values,
-        DataReductionProxyConfig* config,
-        scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+        DataReductionProxyConfig* config)
     : DataReductionProxyConfigServiceClient(params.Pass(),
                                             kTestBackoffPolicy,
                                             request_options,
                                             config_values,
-                                            config,
-                                            io_task_runner),
+                                            config),
       tick_clock_(base::Time::UnixEpoch()),
       test_backoff_entry_(&kTestBackoffPolicy, &tick_clock_) {
 }
@@ -320,10 +316,10 @@
   if (use_test_configurator_) {
     test_context_flags |= USE_TEST_CONFIGURATOR;
     configurator.reset(new TestDataReductionProxyConfigurator(
-        task_runner, net_log.get(), event_store.get()));
+        net_log.get(), event_store.get()));
   } else {
-    configurator.reset(new DataReductionProxyConfigurator(
-        task_runner, net_log.get(), event_store.get()));
+    configurator.reset(
+        new DataReductionProxyConfigurator(net_log.get(), event_store.get()));
   }
 
   scoped_ptr<TestDataReductionProxyConfig> config;
@@ -335,8 +331,7 @@
   if (use_config_client_) {
     test_context_flags |= USE_CONFIG_CLIENT;
     scoped_ptr<DataReductionProxyMutableConfigValues> mutable_config =
-        DataReductionProxyMutableConfigValues::CreateFromParams(task_runner,
-                                                                params.get());
+        DataReductionProxyMutableConfigValues::CreateFromParams(params.get());
     raw_mutable_config = mutable_config.get();
     config.reset(new TestDataReductionProxyConfig(
         mutable_config.Pass(), task_runner, net_log.get(), configurator.get(),
@@ -356,21 +351,21 @@
   if (use_mock_request_options_) {
     test_context_flags |= USE_MOCK_REQUEST_OPTIONS;
     request_options.reset(new MockDataReductionProxyRequestOptions(
-        client_, std::string(), config.get(), task_runner));
+        client_, std::string(), config.get()));
   } else {
-    request_options.reset(new DataReductionProxyRequestOptions(
-        client_, config.get(), task_runner));
+    request_options.reset(
+        new DataReductionProxyRequestOptions(client_, config.get()));
   }
 
   if (use_test_config_client_) {
     test_context_flags |= USE_TEST_CONFIG_CLIENT;
     config_client.reset(new TestDataReductionProxyConfigServiceClient(
-        params.Pass(), request_options.get(), raw_mutable_config, config.get(),
-        task_runner));
+        params.Pass(), request_options.get(), raw_mutable_config,
+        config.get()));
   } else if (use_config_client_) {
     config_client.reset(new DataReductionProxyConfigServiceClient(
         params.Pass(), GetBackoffPolicy(), request_options.get(),
-        raw_mutable_config, config.get(), task_runner));
+        raw_mutable_config, config.get()));
   }
 
   scoped_ptr<DataReductionProxySettings> settings(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 5cc6dfb..7b2add4 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -53,11 +53,9 @@
 class TestDataReductionProxyRequestOptions
     : public DataReductionProxyRequestOptions {
  public:
-  TestDataReductionProxyRequestOptions(
-      Client client,
-      const std::string& version,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  TestDataReductionProxyRequestOptions(Client client,
+                                       const std::string& version,
+                                       DataReductionProxyConfig* config);
 
   // Overrides of DataReductionProxyRequestOptions.
   std::string GetDefaultKey() const override;
@@ -75,11 +73,9 @@
 class MockDataReductionProxyRequestOptions
     : public DataReductionProxyRequestOptions {
  public:
-  MockDataReductionProxyRequestOptions(
-      Client client,
-      const std::string& version,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  MockDataReductionProxyRequestOptions(Client client,
+                                       const std::string& version,
+                                       DataReductionProxyConfig* config);
 
   ~MockDataReductionProxyRequestOptions();
 
@@ -96,8 +92,7 @@
       scoped_ptr<DataReductionProxyParams> params,
       DataReductionProxyRequestOptions* request_options,
       DataReductionProxyMutableConfigValues* config_values,
-      DataReductionProxyConfig* config,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+      DataReductionProxyConfig* config);
 
   ~TestDataReductionProxyConfigServiceClient() override;
 
diff --git a/components/dom_distiller/android/BUILD.gn b/components/dom_distiller/android/BUILD.gn
index 2cc4dc7..833b5da 100644
--- a/components/dom_distiller/android/BUILD.gn
+++ b/components/dom_distiller/android/BUILD.gn
@@ -24,6 +24,7 @@
 android_library("dom_distiller_content_java") {
   deps = [
     ":dom_distiller_core_java",
+    "//base:base_java",
     "//content/public/android:content_java",
   ]
   java_files = [ "java/src/org/chromium/components/dom_distiller/content/DistillablePageUtils.java" ]
diff --git a/components/dom_distiller/content/distillable_page_utils.cc b/components/dom_distiller/content/distillable_page_utils.cc
index 52029922..1267bfb 100644
--- a/components/dom_distiller/content/distillable_page_utils.cc
+++ b/components/dom_distiller/content/distillable_page_utils.cc
@@ -51,9 +51,9 @@
 void IsDistillablePage(content::WebContents* web_contents,
                        base::Callback<void(bool)> callback) {
   switch (GetDistillerHeuristicsType()) {
-    case DistillerHeuristicsType::NONE:
+    case DistillerHeuristicsType::ALWAYS_TRUE:
       base::MessageLoop::current()->PostTask(FROM_HERE,
-                                             base::Bind(callback, false));
+                                             base::Bind(callback, true));
       return;
     case DistillerHeuristicsType::OG_ARTICLE:
       IsOpenGraphArticle(web_contents, callback);
@@ -62,6 +62,11 @@
       IsDistillablePageForDetector(
           web_contents, DistillablePageDetector::GetDefault(), callback);
       return;
+    case DistillerHeuristicsType::NONE:
+    default:
+      base::MessageLoop::current()->PostTask(FROM_HERE,
+                                             base::Bind(callback, false));
+      return;
   }
 }
 
diff --git a/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc b/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc
index 2c84e6b..79d557a 100644
--- a/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc
+++ b/components/dom_distiller/content/distiller_page_web_contents_browsertest.cc
@@ -30,6 +30,46 @@
 using testing::HasSubstr;
 using testing::Not;
 
+namespace {
+
+// Helper class to know how far in the loading process the current WebContents
+// has come. It will call the callback either after
+// DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
+// main frame, based on the value of |wait_for_document_loaded|.
+class WebContentsMainFrameHelper : public content::WebContentsObserver {
+ public:
+  WebContentsMainFrameHelper(content::WebContents* web_contents,
+                             const base::Closure& callback,
+                             bool wait_for_document_loaded)
+      : WebContentsObserver(web_contents),
+        callback_(callback),
+        wait_for_document_loaded_(wait_for_document_loaded) {}
+
+  void DidCommitProvisionalLoadForFrame(
+      content::RenderFrameHost* render_frame_host,
+      const GURL& url,
+      ui::PageTransition transition_type) override {
+    if (wait_for_document_loaded_)
+      return;
+    if (!render_frame_host->GetParent())
+      callback_.Run();
+  }
+
+  void DocumentLoadedInFrame(
+      content::RenderFrameHost* render_frame_host) override {
+    if (wait_for_document_loaded_) {
+      if (!render_frame_host->GetParent())
+        callback_.Run();
+    }
+  }
+
+ private:
+  base::Closure callback_;
+  bool wait_for_document_loaded_;
+};
+
+}  // namespace
+
 namespace dom_distiller {
 
 const char* kSimpleArticlePath = "/simple_article.html";
@@ -130,42 +170,6 @@
   bool new_web_contents_created_;
 };
 
-// Helper class to know how far in the loading process the current WebContents
-// has come. It will call the callback either after
-// DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
-// main frame, based on the value of |wait_for_document_loaded|.
-class WebContentsMainFrameHelper : public content::WebContentsObserver {
- public:
-  WebContentsMainFrameHelper(content::WebContents* web_contents,
-                             const base::Closure& callback,
-                             bool wait_for_document_loaded)
-      : WebContentsObserver(web_contents),
-        callback_(callback),
-        wait_for_document_loaded_(wait_for_document_loaded) {}
-
-  void DidCommitProvisionalLoadForFrame(
-      content::RenderFrameHost* render_frame_host,
-      const GURL& url,
-      ui::PageTransition transition_type) override {
-    if (wait_for_document_loaded_)
-      return;
-    if (!render_frame_host->GetParent())
-      callback_.Run();
-  }
-
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override {
-    if (wait_for_document_loaded_) {
-      if (!render_frame_host->GetParent())
-        callback_.Run();
-    }
-  }
-
- private:
-  base::Closure callback_;
-  bool wait_for_document_loaded_;
-};
-
 IN_PROC_BROWSER_TEST_F(DistillerPageWebContentsTest, BasicDistillationWorks) {
   DistillerPageWebContents distiller_page(
       shell()->web_contents()->GetBrowserContext(),
diff --git a/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc b/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc
new file mode 100644
index 0000000..53b37b6
--- /dev/null
+++ b/components/dom_distiller/content/test/dom_distiller_js_browsertest.cc
@@ -0,0 +1,160 @@
+// 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/callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/dom_distiller/content/web_contents_main_frame_observer.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+// Helper class to know how far in the loading process the current WebContents
+// has come. It will call the callback after DocumentLoadedInFrame is called for
+// the main frame.
+class WebContentsMainFrameHelper : public content::WebContentsObserver {
+ public:
+  WebContentsMainFrameHelper(content::WebContents* web_contents,
+                             const base::Closure& callback)
+      : WebContentsObserver(web_contents), callback_(callback) {}
+
+  void DocumentLoadedInFrame(
+      content::RenderFrameHost* render_frame_host) override {
+    if (!render_frame_host->GetParent())
+      callback_.Run();
+  }
+
+ private:
+  base::Closure callback_;
+};
+
+}  // namespace
+
+namespace dom_distiller {
+
+const char* kExternalTestResourcesPath =
+    "third_party/dom_distiller_js/dist/test/data";
+// TODO(wychen) Remove filter when crbug.com/471854 is fixed.
+const char* kTestFilePath =
+    "/war/test.html?console_log=0&filter=-*.SchemaOrgParserAccessorTest.*";
+const char* kRunJsTestsJs =
+    "(function() {return org.chromium.distiller.JsTestEntry.run();})();";
+
+class DomDistillerJsTest : public content::ContentBrowserTest {
+ public:
+  DomDistillerJsTest() : result_(NULL) {}
+
+  // content::ContentBrowserTest:
+  void SetUpOnMainThread() override {
+    AddComponentsResources();
+    SetUpTestServer();
+    content::ContentBrowserTest::SetUpOnMainThread();
+  }
+
+  void OnJsTestExecutionDone(const base::Value* value) {
+    result_ = value->DeepCopy();
+    js_test_execution_done_callback_.Run();
+  }
+
+ protected:
+  base::Closure js_test_execution_done_callback_;
+  const base::Value* result_;
+
+ private:
+  void AddComponentsResources() {
+    base::FilePath pak_file;
+    base::FilePath pak_dir;
+    PathService::Get(base::DIR_MODULE, &pak_dir);
+    pak_file = pak_dir.Append(FILE_PATH_LITERAL("components_resources.pak"));
+    ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        pak_file, ui::SCALE_FACTOR_NONE);
+  }
+
+  void SetUpTestServer() {
+    base::FilePath path;
+    PathService::Get(base::DIR_SOURCE_ROOT, &path);
+    path = path.AppendASCII(kExternalTestResourcesPath);
+    embedded_test_server()->ServeFilesFromDirectory(path);
+    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(DomDistillerJsTest, RunJsTests) {
+  // Load the test file in content shell and wait until it has fully loaded.
+  content::WebContents* web_contents = shell()->web_contents();
+  dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
+      web_contents);
+  base::RunLoop url_loaded_runner;
+  WebContentsMainFrameHelper main_frame_loaded(web_contents,
+                                               url_loaded_runner.QuitClosure());
+  web_contents->GetController().LoadURL(
+      embedded_test_server()->GetURL(kTestFilePath),
+      content::Referrer(),
+      ui::PAGE_TRANSITION_TYPED,
+      std::string());
+  url_loaded_runner.Run();
+
+  // Execute the JS to run the tests, and wait until it has finished.
+  base::RunLoop run_loop;
+  js_test_execution_done_callback_ = run_loop.QuitClosure();
+  // Add timeout in case JS Test execution fails. It is safe to call the
+  // QuitClosure multiple times.
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(15));
+  web_contents->GetMainFrame()->ExecuteJavaScript(
+      base::UTF8ToUTF16(kRunJsTestsJs),
+      base::Bind(&DomDistillerJsTest::OnJsTestExecutionDone,
+                 base::Unretained(this)));
+  run_loop.Run();
+
+  // By now either the timeout has triggered, or there should be a result.
+  ASSERT_TRUE(result_ != NULL) << "No result found. Timeout?";
+
+  // Convert to dictionary and parse the results.
+  const base::DictionaryValue* dict;
+  result_->GetAsDictionary(&dict);
+  ASSERT_TRUE(result_->GetAsDictionary(&dict));
+
+  ASSERT_TRUE(dict->HasKey("success"));
+  bool success;
+  ASSERT_TRUE(dict->GetBoolean("success", &success));
+
+  ASSERT_TRUE(dict->HasKey("numTests"));
+  int num_tests;
+  ASSERT_TRUE(dict->GetInteger("numTests", &num_tests));
+
+  ASSERT_TRUE(dict->HasKey("failed"));
+  int failed;
+  ASSERT_TRUE(dict->GetInteger("failed", &failed));
+
+  ASSERT_TRUE(dict->HasKey("skipped"));
+  int skipped;
+  ASSERT_TRUE(dict->GetInteger("skipped", &skipped));
+
+  VLOG(0) << "Ran " << num_tests << " tests. failed = " << failed
+          << " skipped = " << skipped;
+  // Ensure that running the tests succeeded.
+  EXPECT_TRUE(success);
+
+  // Only print the log if there was an error.
+  if (!success) {
+    ASSERT_TRUE(dict->HasKey("log"));
+    std::string console_log;
+    ASSERT_TRUE(dict->GetString("log", &console_log));
+    VLOG(0) << "Console log:\n" << console_log;
+  }
+}
+
+}  // namespace dom_distiller
diff --git a/components/dom_distiller/core/css/distilledpage.css b/components/dom_distiller/core/css/distilledpage.css
index 13a3967a..adfb682 100644
--- a/components/dom_distiller/core/css/distilledpage.css
+++ b/components/dom_distiller/core/css/distilledpage.css
@@ -308,19 +308,20 @@
 }
 
 /* Footer feedback form. */
-.contentWrap {
+#contentWrap {
   min-height: 100%;
   overflow: auto;
   padding-bottom: 120px;
 }
 
 .footerFeedback {
+  background-color: #4285F4;
   clear: both;
+  color: #fff;
   display: none;
   height: 120px;
   margin-top: -120px;
-  background-color: #4285F4;
-  color: #fff;
+  width: 100%;
 }
 
 .feedbackContent {
@@ -363,15 +364,17 @@
 /* Feedback fade out */
 .fadeOut {
   animation-duration: 0.5s;
-  animation-name: fadeOutAnimation;
+  animation-name: fadeOutAndSwipeAnimation;
 }
 
-@keyframes fadeOutAnimation {
+@keyframes fadeOutAndSwipeAnimation {
   from {
+    margin-left: 0%;
     opacity: 1;
   }
 
   to {
+    margin-left: -100%;
     opacity: 0;
   }
 }
diff --git a/components/dom_distiller/core/dom_distiller_switches.cc b/components/dom_distiller/core/dom_distiller_switches.cc
index ffb19d3..ad3d693 100644
--- a/components/dom_distiller/core/dom_distiller_switches.cc
+++ b/components/dom_distiller/core/dom_distiller_switches.cc
@@ -9,4 +9,6 @@
     "enable-reader-mode-og-article-heuristics";
 const char kEnableReaderModeAdaBoostHeuristics[] =
     "enable-reader-mode-adaboost-heuristics";
+const char kEnableReaderModeAlwaysTrueHeuristics[] =
+    "enable-reader-mode-always-true-heuristics";
 };
diff --git a/components/dom_distiller/core/dom_distiller_switches.h b/components/dom_distiller/core/dom_distiller_switches.h
index fa632b6..ef8994b 100644
--- a/components/dom_distiller/core/dom_distiller_switches.h
+++ b/components/dom_distiller/core/dom_distiller_switches.h
@@ -12,6 +12,7 @@
 
 extern const char kEnableReaderModeOGArticleHeuristics[];
 extern const char kEnableReaderModeAdaBoostHeuristics[];
+extern const char kEnableReaderModeAlwaysTrueHeuristics[];
 
 };
 
diff --git a/components/dom_distiller/core/experiments.cc b/components/dom_distiller/core/experiments.cc
index b84eb15..102b6d4 100644
--- a/components/dom_distiller/core/experiments.cc
+++ b/components/dom_distiller/core/experiments.cc
@@ -14,8 +14,12 @@
   const std::string group_name =
       base::FieldTrialList::FindFullName("ReaderModeUI");
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableReaderModeOGArticleHeuristics) ||
-      group_name == "OGArticle" || group_name == "ForcedOGArticle") {
+          switches::kEnableReaderModeAlwaysTrueHeuristics) ||
+      group_name == "ForcedAlwaysTrue") {
+    return DistillerHeuristicsType::ALWAYS_TRUE;
+  } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+                 switches::kEnableReaderModeOGArticleHeuristics) ||
+             group_name == "OGArticle" || group_name == "ForcedOGArticle") {
     return DistillerHeuristicsType::OG_ARTICLE;
   } else if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                  switches::kEnableReaderModeAdaBoostHeuristics) ||
diff --git a/components/dom_distiller/core/experiments.h b/components/dom_distiller/core/experiments.h
index 7f1f350f..16edfcb 100644
--- a/components/dom_distiller/core/experiments.h
+++ b/components/dom_distiller/core/experiments.h
@@ -10,6 +10,7 @@
     NONE,
     OG_ARTICLE,
     ADABOOST_MODEL,
+    ALWAYS_TRUE,
   };
 
   DistillerHeuristicsType GetDistillerHeuristicsType();
diff --git a/components/dom_distiller/core/html/dom_distiller_viewer.html b/components/dom_distiller/core/html/dom_distiller_viewer.html
index 02704ea..962df6a 100644
--- a/components/dom_distiller/core/html/dom_distiller_viewer.html
+++ b/components/dom_distiller/core/html/dom_distiller_viewer.html
@@ -13,7 +13,7 @@
   <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 </head>
 <body dir="$8" class="$4">
-  <div class="contentWrap">
+  <div id="contentWrap">
     <div id="mainContent">
       <article>
         <header id="articleHeader">
diff --git a/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
index 39d7f5c..78b67499 100644
--- a/components/dom_distiller/core/javascript/dom_distiller_viewer.js
+++ b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
@@ -156,6 +156,10 @@
 
 document.getElementById('feedbackContainer').addEventListener('animationend',
     function(e) {
-      document.getElementById('feedbackContainer').style.display = "none";
+      document.getElementById('feedbackContainer').style.display = 'none';
+      // Close the gap where the feedback form was.
+      var contentWrap = document.getElementById('contentWrap');
+      contentWrap.style.transition = '0.5s';
+      contentWrap.style.paddingBottom = '0px';
     }, true);
 
diff --git a/components/favicon/OWNERS b/components/favicon/OWNERS
index 38c18f84..a5ad75c0 100644
--- a/components/favicon/OWNERS
+++ b/components/favicon/OWNERS
@@ -1,5 +1,3 @@
+pkotwicz@chromium.org
 sky@chromium.org
 stevenjb@chromium.org
-
-# Temporary for the duration of the favicon componentization.
-sdefresne@chromium.org
diff --git a/components/favicon/ios/OWNERS b/components/favicon/ios/OWNERS
new file mode 100644
index 0000000..378261e88
--- /dev/null
+++ b/components/favicon/ios/OWNERS
@@ -0,0 +1 @@
+sdefresne@chromium.org
\ No newline at end of file
diff --git a/components/history/core/browser/expire_history_backend.cc b/components/history/core/browser/expire_history_backend.cc
index 30eccf5..e39c272 100644
--- a/components/history/core/browser/expire_history_backend.cc
+++ b/components/history/core/browser/expire_history_backend.cc
@@ -157,9 +157,15 @@
   HistoryClient* history_client = GetHistoryClient();
   for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end();
        ++url) {
+    const bool is_bookmarked =
+        history_client && history_client->IsBookmarked(*url);
     URLRow url_row;
-    if (!main_db_->GetRowForURL(*url, &url_row))
-      continue;  // Nothing to delete.
+    if (!main_db_->GetRowForURL(*url, &url_row) && !is_bookmarked) {
+      // If the URL isn't in the database and not bookmarked, we should still
+      // check to see if any favicons need to be deleted.
+      DeleteIcons(*url, &effects);
+      continue;
+    }
 
     // Collect all the visits and delete them. Note that we don't give up if
     // there are no visits, since the URL could still have an entry that we
@@ -173,9 +179,7 @@
     // URL, and not starting with visits in a given time range). We
     // therefore need to call the deletion and favicon update
     // functions manually.
-    DeleteOneURL(url_row,
-                 history_client && history_client->IsBookmarked(*url),
-                 &effects);
+    DeleteOneURL(url_row, is_bookmarked, &effects);
   }
 
   DeleteFaviconsIfPossible(&effects);
@@ -346,25 +350,26 @@
                                         bool is_bookmarked,
                                         DeleteEffects* effects) {
   main_db_->DeleteSegmentForURL(url_row.id());
+  effects->deleted_urls.push_back(url_row);
+  // If the URL is bookmarked we should still keep its favicon around to show
+  // in bookmark-related UI.  We'll delete this icon if the URL is unbookmarked.
+  // (See comments in DeleteURLs().)
+  if (!is_bookmarked)
+    DeleteIcons(url_row.url(), effects);
+  main_db_->DeleteURLRow(url_row.id());
+}
 
-  if (!is_bookmarked) {
-    effects->deleted_urls.push_back(url_row);
-
-    // Delete stuff that references this URL.
-    if (thumb_db_) {
-      // Collect shared information.
-      std::vector<IconMapping> icon_mappings;
-      if (thumb_db_->GetIconMappingsForPageURL(url_row.url(), &icon_mappings)) {
-        for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
-             m != icon_mappings.end(); ++m) {
-          effects->affected_favicons.insert(m->icon_id);
-        }
-        // Delete the mapping entries for the url.
-        thumb_db_->DeleteIconMappings(url_row.url());
-      }
+void ExpireHistoryBackend::DeleteIcons(const GURL& gurl,
+                                       DeleteEffects* effects) {
+  // Collect shared information.
+  std::vector<IconMapping> icon_mappings;
+  if (thumb_db_ && thumb_db_->GetIconMappingsForPageURL(gurl, &icon_mappings)) {
+    for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
+         m != icon_mappings.end(); ++m) {
+      effects->affected_favicons.insert(m->icon_id);
     }
-    // Last, delete the URL entry.
-    main_db_->DeleteURLRow(url_row.id());
+    // Delete the mapping entries for the url.
+    thumb_db_->DeleteIconMappings(gurl);
   }
 }
 
diff --git a/components/history/core/browser/expire_history_backend.h b/components/history/core/browser/expire_history_backend.h
index ac70314..741b8620 100644
--- a/components/history/core/browser/expire_history_backend.h
+++ b/components/history/core/browser/expire_history_backend.h
@@ -145,15 +145,16 @@
   //
   // Assumes the main_db_ is non-NULL.
   //
-  // NOTE: If the url is bookmarked only the segments and text db are updated,
-  // everything else is unchanged. This is done so that bookmarks retain their
-  // favicons and thumbnails.
+  // NOTE: If the url is bookmarked, we keep the favicons and thumbnails.
   void DeleteOneURL(const URLRow& url_row,
                     bool is_bookmarked,
                     DeleteEffects* effects);
 
+  // Deletes all favicons associated with |gurl|.
+  void DeleteIcons(const GURL& gurl, DeleteEffects* effects);
+
   // Deletes all the URLs in the given vector and handles their dependencies.
-  // This will delete starred URLs
+  // This will delete starred URLs.
   void DeleteURLs(const URLRows& urls, DeleteEffects* effects);
 
   // Expiration involves removing visits, then propagating the visits out from
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 4abcca4..6c28260 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -2373,13 +2373,13 @@
     return;
 
   for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
-    URLRow url_row;
-    if (!db_->GetRowForURL(*i, &url_row))
-      continue;  // The URL isn't in the db; nothing to do.
-
     VisitVector visits;
-    db_->GetVisitsForURL(url_row.id(), &visits);
-
+    URLRow url_row;
+    if (db_->GetRowForURL(*i, &url_row))
+      db_->GetVisitsForURL(url_row.id(), &visits);
+    // We need to call DeleteURL() even if the DB didn't contain this URL, so
+    // that we can delete all associated icons in the case of deleting an
+    // unvisited bookmarked URL.
     if (visits.empty())
       expirer_.DeleteURL(*i);  // There are no more visits; nuke the URL.
   }
diff --git a/components/nacl/loader/nonsfi/nonsfi_sandbox.cc b/components/nacl/loader/nonsfi/nonsfi_sandbox.cc
index 0340f6ff..2a6d5bc9 100644
--- a/components/nacl/loader/nonsfi/nonsfi_sandbox.cc
+++ b/components/nacl/loader/nonsfi/nonsfi_sandbox.cc
@@ -10,7 +10,6 @@
 #include <linux/net.h>
 #include <sys/mman.h>
 #include <sys/prctl.h>
-#include <sys/ptrace.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/time.h>
@@ -185,7 +184,8 @@
 void RunSandboxSanityChecks() {
   errno = 0;
   // Make a ptrace request with an invalid PID.
-  long ptrace_ret = ptrace(PTRACE_PEEKUSER, -1 /* pid */, NULL, NULL);
+  long ptrace_ret = syscall(
+      __NR_ptrace, 3 /* = PTRACE_PEEKUSER */, -1 /* pid */, NULL, NULL);
   CHECK_EQ(-1, ptrace_ret);
   // Without the sandbox on, this ptrace call would ESRCH instead.
   CHECK_EQ(EPERM, errno);
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 15d5f7e..e0a57cd 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -218,9 +218,11 @@
   autofill_client_->HideAutofillPopup();
 }
 
-void PasswordAutofillManager::RemoveSuggestion(const base::string16& value,
+bool PasswordAutofillManager::RemoveSuggestion(const base::string16& value,
                                                int identifier) {
-  NOTREACHED();
+  // http://crbug.com/329038
+  NOTIMPLEMENTED();
+  return false;
 }
 
 void PasswordAutofillManager::ClearPreviewedForm() {
diff --git a/components/password_manager/core/browser/password_autofill_manager.h b/components/password_manager/core/browser/password_autofill_manager.h
index dfe0400..08c9842 100644
--- a/components/password_manager/core/browser/password_autofill_manager.h
+++ b/components/password_manager/core/browser/password_autofill_manager.h
@@ -35,7 +35,7 @@
                            int identifier) override;
   void DidAcceptSuggestion(const base::string16& value,
                            int identifier) override;
-  void RemoveSuggestion(const base::string16& value, int identifier) override;
+  bool RemoveSuggestion(const base::string16& value, int identifier) override;
   void ClearPreviewedForm() override;
 
   // Invoked when a password mapping is added.
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 64ade29c..88fdc57 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -16,6 +16,7 @@
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/affiliation_utils.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
@@ -492,10 +493,15 @@
   // Note that we provide the choices but don't actually prefill a value if:
   // (1) we are in Incognito mode, (2) the ACTION paths don't match,
   // or (3) if it matched using public suffix domain matching.
-  bool wait_for_username = client_->IsOffTheRecord() ||
-                           observed_form_.action.GetWithEmptyPath() !=
-                               preferred_match_->action.GetWithEmptyPath() ||
-                           preferred_match_->IsPublicSuffixMatch();
+  // However, 2 and 3 should not apply to Android-based credentials found via
+  // affiliation-based matching (we want to autofill them).
+  // TODO(engedy): Clean this up. See: https://crbug.com/476519.
+  bool wait_for_username =
+      client_->IsOffTheRecord() ||
+      (!IsValidAndroidFacetURI(preferred_match_->original_signon_realm) &&
+       (observed_form_.action.GetWithEmptyPath() !=
+            preferred_match_->action.GetWithEmptyPath() ||
+        preferred_match_->IsPublicSuffixMatch()));
   if (wait_for_username)
     manager_action_ = kManagerActionNone;
   else
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 6d332db..7a20908 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -129,7 +129,7 @@
 class TestPasswordManager : public PasswordManager {
  public:
   explicit TestPasswordManager(TestPasswordManagerClient* client)
-      : PasswordManager(client) {}
+      : PasswordManager(client), wait_for_username_(false) {}
 
   void Autofill(password_manager::PasswordManagerDriver* driver,
                 const autofill::PasswordForm& form_for_autofill,
@@ -137,15 +137,21 @@
                 const autofill::PasswordForm& preferred_match,
                 bool wait_for_username) const override {
     best_matches_ = best_matches;
+    wait_for_username_ = wait_for_username;
   }
 
   const autofill::PasswordFormMap& GetLatestBestMatches() {
     return best_matches_;
   }
 
+  bool GetLatestWaitForUsername() { return wait_for_username_; }
+
  private:
   // Marked mutable to get around constness of Autofill().
+  // TODO(vabr): This should be rewritten as a mock of PasswordManager, and the
+  // interesting arguments should be saved via GMock actions instead.
   mutable autofill::PasswordFormMap best_matches_;
+  mutable bool wait_for_username_;
 };
 
 }  // namespace
@@ -1007,6 +1013,32 @@
                     ->second->original_signon_realm);
 }
 
+TEST_F(PasswordFormManagerTest, AndroidCredentialsAreAutofilled) {
+  EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
+
+  TestPasswordManager password_manager(client());
+  PasswordFormManager manager(&password_manager, client(), client()->driver(),
+                              *observed_form(), false);
+
+  // Although Android-based credentials are treated similarly to PSL-matched
+  // credentials in most respects, they should be autofilled as opposed to be
+  // filled on username-select.
+  ScopedVector<PasswordForm> simulated_results;
+  simulated_results.push_back(new PasswordForm());
+  simulated_results[0]->signon_realm = observed_form()->signon_realm;
+  simulated_results[0]->original_signon_realm =
+      "android://hash@com.google.android";
+  simulated_results[0]->origin = observed_form()->origin;
+  simulated_results[0]->username_value = saved_match()->username_value;
+  simulated_results[0]->password_value = saved_match()->password_value;
+
+  manager.SimulateFetchMatchingLoginsFromPasswordStore();
+  manager.OnGetPasswordStoreResults(simulated_results.Pass());
+
+  EXPECT_EQ(1u, password_manager.GetLatestBestMatches().size());
+  EXPECT_FALSE(password_manager.GetLatestWaitForUsername());
+}
+
 TEST_F(PasswordFormManagerTest, InvalidActionURLsDoNotMatch) {
   PasswordFormManager manager(nullptr, client(), kNoDriver, *observed_form(),
                               false);
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index a10b7e8..f4cd0658 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -75,11 +75,13 @@
 
 void PasswordStore::SetAffiliatedMatchHelper(
     scoped_ptr<AffiliatedMatchHelper> helper) {
-  DCHECK(helper);
-  DCHECK(!affiliated_match_helper_);
   affiliated_match_helper_ = helper.Pass();
 }
 
+bool PasswordStore::HasAffiliatedMatchHelper() const {
+  return affiliated_match_helper_;
+}
+
 void PasswordStore::AddLogin(const PasswordForm& form) {
   CheckForEmptyUsernameAndPassword(form);
   ScheduleTask(base::Bind(&PasswordStore::AddLoginInternal, this, form));
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index b4ba7b4..852a51e 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -68,11 +68,15 @@
 
   // Sets the affiliation-based match |helper| that will be used by subsequent
   // GetLogins() calls to return credentials stored not only for the requested
-  // sign-on realm, but also for affiliated Android applications. The helper
-  // must already be initialized. Unless a |helper| is set, affiliation-based
-  // matching is disabled.
+  // sign-on realm, but also for affiliated Android applications. If |helper| is
+  // null, clears the the currently set helper if any. Unless a helper is set,
+  // affiliation-based matching is disabled. The passed |helper| must already be
+  // initialized if it is non-null.
   void SetAffiliatedMatchHelper(scoped_ptr<AffiliatedMatchHelper> helper);
 
+  // Returns whether or not an affiliation-based match helper is set.
+  bool HasAffiliatedMatchHelper() const;
+
   // Adds the given PasswordForm to the secure password store asynchronously.
   virtual void AddLogin(const autofill::PasswordForm& form);
 
diff --git a/components/plugins/OWNERS b/components/plugins/OWNERS
index 00985a81..21efaa10 100644
--- a/components/plugins/OWNERS
+++ b/components/plugins/OWNERS
@@ -1 +1,2 @@
 bauerb@chromium.org
+tommycli@chromium.org
diff --git a/components/proximity_auth.gypi b/components/proximity_auth.gypi
index fe5f3be2..c4e2503 100644
--- a/components/proximity_auth.gypi
+++ b/components/proximity_auth.gypi
@@ -79,6 +79,9 @@
         "proximity_auth/cryptauth/cryptauth_client.h",
         "proximity_auth/cryptauth/cryptauth_client_impl.cc",
         "proximity_auth/cryptauth/cryptauth_client_impl.h",
+        "proximity_auth/cryptauth/cryptauth_enroller.h",
+        "proximity_auth/cryptauth/cryptauth_enroller_impl.cc",
+        "proximity_auth/cryptauth/cryptauth_enroller_impl.h",
         "proximity_auth/cryptauth/cryptauth_enrollment_utils.cc",
         "proximity_auth/cryptauth/cryptauth_enrollment_utils.h",
         "proximity_auth/cryptauth/secure_message_delegate.cc",
@@ -97,10 +100,13 @@
       'dependencies': [
         'cryptauth_proto',
         '../base/base.gyp:base',
+        '../testing/gmock.gyp:gmock',
       ],
       'sources': [
         "proximity_auth/cryptauth/fake_secure_message_delegate.cc",
         "proximity_auth/cryptauth/fake_secure_message_delegate.h",
+        "proximity_auth/cryptauth/mock_cryptauth_client.cc",
+        "proximity_auth/cryptauth/mock_cryptauth_client.h",
       ],
       'export_dependent_settings': [
         'cryptauth_proto',
diff --git a/components/proximity_auth/cryptauth/BUILD.gn b/components/proximity_auth/cryptauth/BUILD.gn
index bf255eb6..fcbf846 100644
--- a/components/proximity_auth/cryptauth/BUILD.gn
+++ b/components/proximity_auth/cryptauth/BUILD.gn
@@ -14,6 +14,9 @@
     "cryptauth_client.h",
     "cryptauth_client_impl.cc",
     "cryptauth_client_impl.h",
+    "cryptauth_enroller.h",
+    "cryptauth_enroller_impl.cc",
+    "cryptauth_enroller_impl.h",
     "cryptauth_enrollment_utils.cc",
     "cryptauth_enrollment_utils.h",
     "secure_message_delegate.cc",
@@ -32,14 +35,19 @@
 }
 
 source_set("test_support") {
+  testonly = true
+
   sources = [
     "fake_secure_message_delegate.cc",
     "fake_secure_message_delegate.h",
+    "mock_cryptauth_client.cc",
+    "mock_cryptauth_client.h",
   ]
 
   deps = [
     ":cryptauth",
     "//base",
+    "//testing/gmock",
   ]
 
   public_deps = [
@@ -54,6 +62,7 @@
     "cryptauth_access_token_fetcher_impl_unittest.cc",
     "cryptauth_api_call_flow_unittest.cc",
     "cryptauth_client_impl_unittest.cc",
+    "cryptauth_enroller_impl_unittest.cc",
     "fake_secure_message_delegate_unittest.cc",
   ]
 
diff --git a/components/proximity_auth/cryptauth/cryptauth_enroller.h b/components/proximity_auth/cryptauth/cryptauth_enroller.h
new file mode 100644
index 0000000..99f7b04f
--- /dev/null
+++ b/components/proximity_auth/cryptauth/cryptauth_enroller.h
@@ -0,0 +1,29 @@
+// 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_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_H
+#define COMPONENTS_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_H
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace proximity_auth {
+
+// Interface for enrolling a device with CryptAuth.
+class CryptAuthEnroller {
+ public:
+  // Enrolls the device with |device_info| properties for a given
+  // |invocation_reason|. |callback| will be called with true if the enrollment
+  // succeeds and false otherwise.
+  typedef base::Callback<void(bool)> EnrollmentFinishedCallback;
+  virtual void Enroll(const cryptauth::GcmDeviceInfo& device_info,
+                      cryptauth::InvocationReason invocation_reason,
+                      const EnrollmentFinishedCallback& callback) = 0;
+};
+
+}  // namespace proximity_auth
+
+#endif  // COMPONENTS_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_H
diff --git a/components/proximity_auth/cryptauth/cryptauth_enroller_impl.cc b/components/proximity_auth/cryptauth/cryptauth_enroller_impl.cc
new file mode 100644
index 0000000..729648d
--- /dev/null
+++ b/components/proximity_auth/cryptauth/cryptauth_enroller_impl.cc
@@ -0,0 +1,202 @@
+// 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/proximity_auth/cryptauth/cryptauth_enroller_impl.h"
+
+#include "base/bind.h"
+#include "components/proximity_auth/cryptauth/cryptauth_client.h"
+#include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h"
+#include "components/proximity_auth/cryptauth/secure_message_delegate.h"
+
+namespace proximity_auth {
+
+namespace {
+
+// A successful SetupEnrollment or FinishEnrollment response should contain this
+// status string.
+const char kResponseStatusOk[] = "OK";
+
+// The name of the "gcmV1" protocol that the enrolling device supports.
+const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
+
+// The version field of the GcmMetadata message.
+const int kGCMMetadataVersion = 1;
+
+// Returns true if |device_info| contains the required fields for enrollment.
+bool ValidateDeviceInfo(const cryptauth::GcmDeviceInfo& device_info) {
+  if (!device_info.has_user_public_key()) {
+    VLOG(1) << "Expected user_public_key field in GcmDeviceInfo.";
+    return false;
+  }
+
+  if (!device_info.has_key_handle()) {
+    VLOG(1) << "Expected key_handle field in GcmDeviceInfo.";
+    return false;
+  }
+
+  if (!device_info.has_long_device_id()) {
+    VLOG(1) << "Expected long_device_id field in GcmDeviceInfo.";
+    return false;
+  }
+
+  if (!device_info.has_device_type()) {
+    VLOG(1) << "Expected device_type field in GcmDeviceInfo.";
+    return false;
+  }
+
+  return true;
+}
+
+// Creates the public metadata to put in the SecureMessage that is sent to the
+// server with the FinishEnrollment request.
+std::string CreateEnrollmentPublicMetadata() {
+  cryptauth::GcmMetadata metadata;
+  metadata.set_version(kGCMMetadataVersion);
+  metadata.set_type(cryptauth::MessageType::ENROLLMENT);
+  return metadata.SerializeAsString();
+}
+
+}  // namespace
+
+CryptAuthEnrollerImpl::CryptAuthEnrollerImpl(
+    scoped_ptr<CryptAuthClientFactory> client_factory,
+    scoped_ptr<SecureMessageDelegate> secure_message_delegate_)
+    : client_factory_(client_factory.Pass()),
+      secure_message_delegate_(secure_message_delegate_.Pass()),
+      weak_ptr_factory_(this) {
+}
+
+CryptAuthEnrollerImpl::~CryptAuthEnrollerImpl() {
+}
+
+void CryptAuthEnrollerImpl::Enroll(
+    const cryptauth::GcmDeviceInfo& device_info,
+    cryptauth::InvocationReason invocation_reason,
+    const EnrollmentFinishedCallback& callback) {
+  if (!callback_.is_null()) {
+    VLOG(1) << "Enroll() already called. Do not reuse.";
+    callback.Run(false);
+    return;
+  }
+
+  device_info_ = device_info;
+  invocation_reason_ = invocation_reason;
+  callback_ = callback;
+
+  if (!ValidateDeviceInfo(device_info)) {
+    callback.Run(false);
+    return;
+  }
+
+  secure_message_delegate_->GenerateKeyPair(
+      base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key,
+                                               const std::string& private_key) {
+  session_public_key_ = public_key;
+  session_private_key_ = private_key;
+
+  cryptauth_client_ = client_factory_->CreateInstance();
+  cryptauth::SetupEnrollmentRequest request;
+  request.add_types(kSupportedEnrollmentTypeGcmV1);
+  request.set_invocation_reason(invocation_reason_);
+  cryptauth_client_->SetupEnrollment(
+      request, base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
+    const cryptauth::SetupEnrollmentResponse& response) {
+  if (response.status() != kResponseStatusOk) {
+    VLOG(1) << "Unexpected status for SetupEnrollment: " << response.status();
+    callback_.Run(false);
+    return;
+  }
+
+  if (response.infos_size() == 0) {
+    VLOG(1) << "No response info returned by server for SetupEnrollment";
+    callback_.Run(false);
+    return;
+  }
+
+  setup_info_ = response.infos(0);
+  device_info_.set_enrollment_session_id(setup_info_.enrollment_session_id());
+  secure_message_delegate_->DeriveKey(
+      session_private_key_, setup_info_.server_ephemeral_key(),
+      base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) {
+  VLOG(1) << "SetupEnrollment API failed with error: " << error;
+  callback_.Run(false);
+}
+
+void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) {
+  symmetric_key_ = symmetric_key;
+  SecureMessageDelegate::CreateOptions options;
+  options.encryption_scheme = securemessage::NONE;
+  options.signature_scheme = securemessage::ECDSA_P256_SHA256;
+  options.verification_key_id = session_public_key_;
+
+  // The inner message contains the signed device information that will be
+  // sent to CryptAuth.
+  secure_message_delegate_->CreateSecureMessage(
+      device_info_.SerializeAsString(), session_private_key_, options,
+      base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated(
+    const std::string& inner_message) {
+  SecureMessageDelegate::CreateOptions options;
+  options.encryption_scheme = securemessage::AES_256_CBC;
+  options.signature_scheme = securemessage::HMAC_SHA256;
+  options.public_metadata = CreateEnrollmentPublicMetadata();
+
+  // The outer message encrypts and signs the inner message with the derived
+  // symmetric session key.
+  secure_message_delegate_->CreateSecureMessage(
+      inner_message, symmetric_key_, options,
+      base::Bind(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated(
+    const std::string& outer_message) {
+  cryptauth::FinishEnrollmentRequest request;
+  request.set_enrollment_session_id(setup_info_.enrollment_session_id());
+  request.set_enrollment_message(outer_message);
+  request.set_device_ephemeral_key(session_public_key_);
+  request.set_invocation_reason(invocation_reason_);
+
+  cryptauth_client_ = client_factory_->CreateInstance();
+  cryptauth_client_->FinishEnrollment(
+      request, base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
+    const cryptauth::FinishEnrollmentResponse& response) {
+  if (response.status() != kResponseStatusOk) {
+    VLOG(1) << "Unexpected status for FinishEnrollment: " << response.status();
+    callback_.Run(false);
+  } else {
+    callback_.Run(true);
+  }
+}
+
+void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
+    const std::string& error) {
+  VLOG(1) << "FinishEnrollment API failed with error: " << error;
+  callback_.Run(false);
+}
+
+}  // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/cryptauth_enroller_impl.h b/components/proximity_auth/cryptauth/cryptauth_enroller_impl.h
new file mode 100644
index 0000000..07da5ed
--- /dev/null
+++ b/components/proximity_auth/cryptauth/cryptauth_enroller_impl.h
@@ -0,0 +1,95 @@
+// 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_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_IMPL_H
+#define COMPONENTS_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_IMPL_H
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/proximity_auth/cryptauth/cryptauth_enroller.h"
+
+namespace proximity_auth {
+
+class CryptAuthClient;
+class CryptAuthClientFactory;
+class SecureMessageDelegate;
+
+// Implementation of CryptAuthEnroller to perform enrollment in two steps:
+// 1. SetupEnrollment:
+//     Obtain a session public key from CryptAuth used to encrypt enrollment
+//     data. Generate an ephemeral public key and derive a session symmetric
+//     key.
+// 2. FinishEnrollment:
+//     Encrypt the enrollment data with the session symmetric key, and send the
+//     payload and device's public key to CryptAuth.
+class CryptAuthEnrollerImpl : public CryptAuthEnroller {
+ public:
+  // |client_factory| creates CryptAuthClient instances for making API calls.
+  // |crypto_delegate| is responsible for SecureMessage operations.
+  CryptAuthEnrollerImpl(
+      scoped_ptr<CryptAuthClientFactory> client_factory,
+      scoped_ptr<SecureMessageDelegate> secure_message_delegate_);
+  ~CryptAuthEnrollerImpl();
+
+  // CryptAuthEnroller:
+  void Enroll(const cryptauth::GcmDeviceInfo& device_info,
+              cryptauth::InvocationReason invocation_reason,
+              const EnrollmentFinishedCallback& callback) override;
+
+ private:
+  // Callbacks for SetupEnrollment.
+  void OnSetupEnrollmentSuccess(
+      const cryptauth::SetupEnrollmentResponse& response);
+  void OnSetupEnrollmentFailure(const std::string& error);
+
+  // Callbacks for FinishEnrollment.
+  void OnFinishEnrollmentSuccess(
+      const cryptauth::FinishEnrollmentResponse& response);
+  void OnFinishEnrollmentFailure(const std::string& error);
+
+  // Callbacks for SecureMessageDelegate operations.
+  void OnKeyPairGenerated(const std::string& public_key,
+                          const std::string& private_key);
+  void OnKeyDerived(const std::string& symmetric_key);
+  void OnInnerSecureMessageCreated(const std::string& inner_message);
+  void OnOuterSecureMessageCreated(const std::string& outer_message);
+
+  // Creates the CryptAuthClient instances to make API requests.
+  scoped_ptr<CryptAuthClientFactory> client_factory_;
+
+  // Handles SecureMessage operations.
+  scoped_ptr<SecureMessageDelegate> secure_message_delegate_;
+
+  // The CryptAuthClient for the latest request.
+  scoped_ptr<CryptAuthClient> cryptauth_client_;
+
+  // The ephemeral key-pair generated for a single enrollment.
+  std::string session_public_key_;
+  std::string session_private_key_;
+
+  // Contains information of the device to enroll.
+  cryptauth::GcmDeviceInfo device_info_;
+
+  // The reason telling the server why the enrollment happened.
+  cryptauth::InvocationReason invocation_reason_;
+
+  // The setup information returned from the SetupEnrollment API call.
+  cryptauth::SetupEnrollmentInfo setup_info_;
+
+  // Callback invoked when the enrollment is done.
+  EnrollmentFinishedCallback callback_;
+
+  // The derived ephemeral symmetric key.
+  std::string symmetric_key_;
+
+  base::WeakPtrFactory<CryptAuthEnrollerImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollerImpl);
+};
+
+}  // namespace proximity_auth
+
+#endif  // COMPONENTS_PROXIMITY_AUTH_CRYPTAUTH_ENROLLER_IMPL_H
diff --git a/components/proximity_auth/cryptauth/cryptauth_enroller_impl_unittest.cc b/components/proximity_auth/cryptauth/cryptauth_enroller_impl_unittest.cc
new file mode 100644
index 0000000..bee79e43
--- /dev/null
+++ b/components/proximity_auth/cryptauth/cryptauth_enroller_impl_unittest.cc
@@ -0,0 +1,338 @@
+// 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/proximity_auth/cryptauth/cryptauth_enroller_impl.h"
+
+#include "base/bind.h"
+#include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h"
+#include "components/proximity_auth/cryptauth/fake_secure_message_delegate.h"
+#include "components/proximity_auth/cryptauth/mock_cryptauth_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace proximity_auth {
+
+namespace {
+
+const char kClientSessionPublicKey[] = "throw away after one use";
+const char kServerSessionPublicKey[] = "disposables are not eco-friendly";
+const char kClientPersistentPublicKey[] = "saves 50 trees a year";
+
+cryptauth::InvocationReason kInvocationReason =
+    cryptauth::INVOCATION_REASON_MANUAL;
+const int kGCMMetadataVersion = 1;
+const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
+const char kResponseStatusOk[] = "OK";
+const char kResponseStatusNotOk[] = "Your key was too bland.";
+const char kEnrollmentSessionId[] = "0123456789876543210";
+const char kFinishEnrollmentError[] = "A hungry router ate all your packets.";
+
+const char kDeviceId[] = "2015 AD";
+const cryptauth::DeviceType kDeviceType = cryptauth::CHROME;
+const char kDeviceOsVersion[] = "41.0.0";
+
+// Creates and returns the GcmDeviceInfo message to be uploaded.
+cryptauth::GcmDeviceInfo GetDeviceInfo() {
+  cryptauth::GcmDeviceInfo device_info;
+  device_info.set_long_device_id(kDeviceId);
+  device_info.set_device_type(kDeviceType);
+  device_info.set_device_os_version(kDeviceOsVersion);
+  device_info.set_user_public_key(kClientPersistentPublicKey);
+  device_info.set_key_handle(kClientPersistentPublicKey);
+  return device_info;
+}
+
+// Creates and returns the SetupEnrollmentResponse message to be returned to the
+// enroller with the session_. If |success| is false, then a bad response will
+// be returned.
+cryptauth::SetupEnrollmentResponse GetSetupEnrollmentResponse(bool success) {
+  cryptauth::SetupEnrollmentResponse response;
+  if (!success) {
+    response.set_status(kResponseStatusNotOk);
+    return response;
+  }
+
+  response.set_status(kResponseStatusOk);
+  cryptauth::SetupEnrollmentInfo* info = response.add_infos();
+  info->set_type(kSupportedEnrollmentTypeGcmV1);
+  info->set_enrollment_session_id(kEnrollmentSessionId);
+  info->set_server_ephemeral_key(kServerSessionPublicKey);
+  return response;
+}
+
+// Creates and returns the FinishEnrollmentResponse message to be returned to
+// the enroller with the session_. If |success| is false, then a bad response
+// will be returned.
+cryptauth::FinishEnrollmentResponse GetFinishEnrollmentResponse(bool success) {
+  cryptauth::FinishEnrollmentResponse response;
+  if (success) {
+    response.set_status(kResponseStatusOk);
+  } else {
+    response.set_status(kResponseStatusNotOk);
+    response.set_error_message(kFinishEnrollmentError);
+  }
+  return response;
+}
+
+// Callback that saves the key returned by SecureMessageDelegate::DeriveKey().
+void SaveDerivedKey(std::string* value_out, const std::string& value) {
+  *value_out = value;
+}
+
+// Callback that saves the results returned by
+// SecureMessageDelegate::UnwrapSecureMessage().
+void SaveUnwrapResults(bool* verified_out,
+                       std::string* payload_out,
+                       securemessage::Header* header_out,
+                       bool verified,
+                       const std::string& payload,
+                       const securemessage::Header& header) {
+  *verified_out = verified;
+  *payload_out = payload;
+  *header_out = header;
+}
+
+}  // namespace
+
+class ProximityAuthCryptAuthEnrollerTest
+    : public testing::Test,
+      public MockCryptAuthClientFactory::Observer {
+ public:
+  ProximityAuthCryptAuthEnrollerTest()
+      : client_factory_(new MockCryptAuthClientFactory(false)),
+        secure_message_delegate_(new FakeSecureMessageDelegate()),
+        enroller_(make_scoped_ptr(client_factory_),
+                  make_scoped_ptr(secure_message_delegate_)) {
+    client_factory_->AddObserver(this);
+  }
+
+  // Starts the enroller.
+  void StartEnroller(const cryptauth::GcmDeviceInfo& device_info) {
+    secure_message_delegate_->set_next_public_key(kClientSessionPublicKey);
+    enroller_result_.reset();
+    enroller_.Enroll(
+        device_info, kInvocationReason,
+        base::Bind(&ProximityAuthCryptAuthEnrollerTest::OnEnrollerCompleted,
+                   base::Unretained(this)));
+  }
+
+  // Verifies that |serialized_message| is a valid SecureMessage sent with the
+  // FinishEnrollment API call.
+  void ValidateEnrollmentMessage(const std::string& serialized_message) {
+    // Derive the session symmetric key.
+    std::string server_session_private_key =
+        secure_message_delegate_->GetPrivateKeyForPublicKey(
+            kServerSessionPublicKey);
+    std::string symmetric_key;
+    secure_message_delegate_->DeriveKey(
+        server_session_private_key, kClientSessionPublicKey,
+        base::Bind(&SaveDerivedKey, &symmetric_key));
+
+    std::string inner_message;
+    std::string inner_payload;
+    {
+      // Unwrap the outer message.
+      bool verified;
+      securemessage::Header header;
+      SecureMessageDelegate::UnwrapOptions unwrap_options;
+      unwrap_options.encryption_scheme = securemessage::AES_256_CBC;
+      unwrap_options.signature_scheme = securemessage::HMAC_SHA256;
+      secure_message_delegate_->UnwrapSecureMessage(
+          serialized_message, symmetric_key, unwrap_options,
+          base::Bind(&SaveUnwrapResults, &verified, &inner_message, &header));
+      EXPECT_TRUE(verified);
+
+      cryptauth::GcmMetadata metadata;
+      ASSERT_TRUE(metadata.ParseFromString(header.public_metadata()));
+      EXPECT_EQ(kGCMMetadataVersion, metadata.version());
+      EXPECT_EQ(cryptauth::MessageType::ENROLLMENT, metadata.type());
+    }
+
+    {
+      // Unwrap inner message.
+      bool verified;
+      securemessage::Header header;
+      SecureMessageDelegate::UnwrapOptions unwrap_options;
+      unwrap_options.encryption_scheme = securemessage::NONE;
+      unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256;
+      secure_message_delegate_->UnwrapSecureMessage(
+          inner_message, kClientSessionPublicKey, unwrap_options,
+          base::Bind(&SaveUnwrapResults, &verified, &inner_payload, &header));
+      EXPECT_TRUE(verified);
+      EXPECT_EQ(kClientSessionPublicKey, header.verification_key_id());
+    }
+
+    // Check that the decrypted GcmDeviceInfo is correct.
+    cryptauth::GcmDeviceInfo device_info;
+    ASSERT_TRUE(device_info.ParseFromString(inner_payload));
+    EXPECT_EQ(kDeviceId, device_info.long_device_id());
+    EXPECT_EQ(kDeviceType, device_info.device_type());
+    EXPECT_EQ(kDeviceOsVersion, device_info.device_os_version());
+    EXPECT_EQ(kClientPersistentPublicKey, device_info.user_public_key());
+    EXPECT_EQ(kClientPersistentPublicKey, device_info.key_handle());
+    EXPECT_EQ(kEnrollmentSessionId, device_info.enrollment_session_id());
+  }
+
+ protected:
+  // MockCryptAuthClientFactory::Observer:
+  void OnCryptAuthClientCreated(MockCryptAuthClient* client) override {
+    ON_CALL(*client, SetupEnrollment(_, _, _))
+        .WillByDefault(Invoke(
+            this, &ProximityAuthCryptAuthEnrollerTest::OnSetupEnrollment));
+
+    ON_CALL(*client, FinishEnrollment(_, _, _))
+        .WillByDefault(Invoke(
+            this, &ProximityAuthCryptAuthEnrollerTest::OnFinishEnrollment));
+  }
+
+  void OnEnrollerCompleted(bool success) {
+    EXPECT_FALSE(enroller_result_.get());
+    enroller_result_.reset(new bool(success));
+  }
+
+  void OnSetupEnrollment(
+      const cryptauth::SetupEnrollmentRequest& request,
+      const CryptAuthClient::SetupEnrollmentCallback& callback,
+      const CryptAuthClient::ErrorCallback& error_callback) {
+    // Check that SetupEnrollment is called before FinishEnrollment.
+    EXPECT_FALSE(setup_request_.get());
+    EXPECT_FALSE(finish_request_.get());
+    EXPECT_TRUE(setup_callback_.is_null());
+    EXPECT_TRUE(error_callback_.is_null());
+
+    setup_request_.reset(new cryptauth::SetupEnrollmentRequest(request));
+    setup_callback_ = callback;
+    error_callback_ = error_callback;
+  }
+
+  void OnFinishEnrollment(
+      const cryptauth::FinishEnrollmentRequest& request,
+      const CryptAuthClient::FinishEnrollmentCallback& callback,
+      const CryptAuthClient::ErrorCallback& error_callback) {
+    // Check that FinishEnrollment is called after SetupEnrollment.
+    EXPECT_TRUE(setup_request_.get());
+    EXPECT_FALSE(finish_request_.get());
+    EXPECT_TRUE(finish_callback_.is_null());
+
+    finish_request_.reset(new cryptauth::FinishEnrollmentRequest(request));
+    finish_callback_ = callback;
+    error_callback_ = error_callback;
+  }
+
+  // Owned by |enroller_|.
+  MockCryptAuthClientFactory* client_factory_;
+  // Owned by |enroller_|.
+  FakeSecureMessageDelegate* secure_message_delegate_;
+  // The CryptAuthEnroller under test.
+  CryptAuthEnrollerImpl enroller_;
+
+  // Stores the result of running |enroller_|.
+  scoped_ptr<bool> enroller_result_;
+
+  // Stored callbacks and requests for SetupEnrollment and FinishEnrollment.
+  scoped_ptr<cryptauth::SetupEnrollmentRequest> setup_request_;
+  scoped_ptr<cryptauth::FinishEnrollmentRequest> finish_request_;
+  CryptAuthClient::SetupEnrollmentCallback setup_callback_;
+  CryptAuthClient::FinishEnrollmentCallback finish_callback_;
+  CryptAuthClient::ErrorCallback error_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProximityAuthCryptAuthEnrollerTest);
+};
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, EnrollmentSucceeds) {
+  StartEnroller(GetDeviceInfo());
+
+  // Handle SetupEnrollment request.
+  EXPECT_TRUE(setup_request_.get());
+  EXPECT_EQ(kInvocationReason, setup_request_->invocation_reason());
+  ASSERT_EQ(1, setup_request_->types_size());
+  EXPECT_EQ(kSupportedEnrollmentTypeGcmV1, setup_request_->types(0));
+  ASSERT_FALSE(setup_callback_.is_null());
+  setup_callback_.Run(GetSetupEnrollmentResponse(true));
+
+  // Handle FinishEnrollment request.
+  EXPECT_TRUE(finish_request_.get());
+  EXPECT_EQ(kEnrollmentSessionId, finish_request_->enrollment_session_id());
+  EXPECT_EQ(kClientSessionPublicKey, finish_request_->device_ephemeral_key());
+  ValidateEnrollmentMessage(finish_request_->enrollment_message());
+  EXPECT_EQ(kInvocationReason, finish_request_->invocation_reason());
+
+  ASSERT_FALSE(finish_callback_.is_null());
+  finish_callback_.Run(GetFinishEnrollmentResponse(true));
+
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_TRUE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, SetupEnrollmentApiCallError) {
+  StartEnroller(GetDeviceInfo());
+
+  EXPECT_TRUE(setup_request_.get());
+  ASSERT_FALSE(error_callback_.is_null());
+  error_callback_.Run("Setup enrollment failed network");
+
+  EXPECT_TRUE(finish_callback_.is_null());
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, SetupEnrollmentBadStatus) {
+  StartEnroller(GetDeviceInfo());
+
+  EXPECT_TRUE(setup_request_.get());
+  setup_callback_.Run(GetSetupEnrollmentResponse(false));
+
+  EXPECT_TRUE(finish_callback_.is_null());
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, SetupEnrollmentNoInfosReturned) {
+  StartEnroller(GetDeviceInfo());
+  EXPECT_TRUE(setup_request_.get());
+  cryptauth::SetupEnrollmentResponse response;
+  response.set_status(kResponseStatusOk);
+  setup_callback_.Run(response);
+
+  EXPECT_TRUE(finish_callback_.is_null());
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, FinishEnrollmentApiCallError) {
+  StartEnroller(GetDeviceInfo());
+  setup_callback_.Run(GetSetupEnrollmentResponse(true));
+  ASSERT_FALSE(error_callback_.is_null());
+  error_callback_.Run("finish enrollment oauth error");
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, FinishEnrollmentBadStatus) {
+  StartEnroller(GetDeviceInfo());
+  setup_callback_.Run(GetSetupEnrollmentResponse(true));
+  ASSERT_FALSE(finish_callback_.is_null());
+  finish_callback_.Run(GetFinishEnrollmentResponse(false));
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, ReuseEnroller) {
+  StartEnroller(GetDeviceInfo());
+  setup_callback_.Run(GetSetupEnrollmentResponse(true));
+  finish_callback_.Run(GetFinishEnrollmentResponse(true));
+  EXPECT_TRUE(*enroller_result_);
+
+  StartEnroller(GetDeviceInfo());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+TEST_F(ProximityAuthCryptAuthEnrollerTest, IncompleteDeviceInfo) {
+  StartEnroller(cryptauth::GcmDeviceInfo());
+  ASSERT_TRUE(enroller_result_.get());
+  EXPECT_FALSE(*enroller_result_);
+}
+
+}  // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/fake_secure_message_delegate.cc b/components/proximity_auth/cryptauth/fake_secure_message_delegate.cc
index 195c24b..5fa5c5f 100644
--- a/components/proximity_auth/cryptauth/fake_secure_message_delegate.cc
+++ b/components/proximity_auth/cryptauth/fake_secure_message_delegate.cc
@@ -189,4 +189,9 @@
   }
 }
 
+std::string FakeSecureMessageDelegate::GetPrivateKeyForPublicKey(
+    const std::string& public_key) {
+  return kPrivateKeyPrefix + public_key;
+}
+
 }  // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/fake_secure_message_delegate.h b/components/proximity_auth/cryptauth/fake_secure_message_delegate.h
index ad8b21b4..1fe9c0a 100644
--- a/components/proximity_auth/cryptauth/fake_secure_message_delegate.h
+++ b/components/proximity_auth/cryptauth/fake_secure_message_delegate.h
@@ -34,6 +34,9 @@
       const UnwrapOptions& unwrap_options,
       const UnwrapSecureMessageCallback& callback) override;
 
+  // Returns the corresponding private key for the given |public_key|.
+  std::string GetPrivateKeyForPublicKey(const std::string& public_key);
+
   // Sets the next public key to be returned by GenerateKeyPair(). The
   // corresponding private key will be derived from this public key.
   void set_next_public_key(const std::string& public_key) {
diff --git a/components/proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc b/components/proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc
index 12a02cd..6c35a7fe 100644
--- a/components/proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc
+++ b/components/proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc
@@ -193,4 +193,13 @@
   EXPECT_EQ(kPayload, payload);
 }
 
+TEST_F(ProximityAuthFakeSecureMessageDelegateTest, GetPrivateKeyForPublicKey) {
+  delegate_.set_next_public_key(kTestPublicKey);
+  std::string public_key, private_key;
+  delegate_.GenerateKeyPair(
+      base::Bind(&SaveKeyPair, &public_key, &private_key));
+  EXPECT_EQ(kTestPublicKey, public_key);
+  EXPECT_EQ(private_key, delegate_.GetPrivateKeyForPublicKey(kTestPublicKey));
+}
+
 }  // proximity_auth
diff --git a/components/proximity_auth/cryptauth/mock_cryptauth_client.cc b/components/proximity_auth/cryptauth/mock_cryptauth_client.cc
new file mode 100644
index 0000000..099082fc
--- /dev/null
+++ b/components/proximity_auth/cryptauth/mock_cryptauth_client.cc
@@ -0,0 +1,43 @@
+// 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/callback.h"
+#include "components/proximity_auth/cryptauth/mock_cryptauth_client.h"
+
+namespace proximity_auth {
+
+MockCryptAuthClient::MockCryptAuthClient() {
+}
+
+MockCryptAuthClient::~MockCryptAuthClient() {
+}
+
+MockCryptAuthClientFactory::MockCryptAuthClientFactory(bool is_strict)
+    : is_strict_(is_strict) {
+}
+
+MockCryptAuthClientFactory::~MockCryptAuthClientFactory() {
+}
+
+scoped_ptr<CryptAuthClient> MockCryptAuthClientFactory::CreateInstance() {
+  scoped_ptr<MockCryptAuthClient> client;
+  if (is_strict_)
+    client.reset(new testing::StrictMock<MockCryptAuthClient>());
+  else
+    client.reset(new testing::NiceMock<MockCryptAuthClient>());
+
+  FOR_EACH_OBSERVER(Observer, observer_list_,
+                    OnCryptAuthClientCreated(client.get()));
+  return client.Pass();
+}
+
+void MockCryptAuthClientFactory::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void MockCryptAuthClientFactory::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+}  // namespace proximity_auth
diff --git a/components/proximity_auth/cryptauth/mock_cryptauth_client.h b/components/proximity_auth/cryptauth/mock_cryptauth_client.h
new file mode 100644
index 0000000..de818ecf
--- /dev/null
+++ b/components/proximity_auth/cryptauth/mock_cryptauth_client.h
@@ -0,0 +1,83 @@
+// 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_PROXIMITY_AUTH_MOCK_CRYPTAUTH_CLIENT_H
+#define COMPONENTS_PROXIMITY_AUTH_MOCK_CRYPTAUTH_CLIENT_H
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/proximity_auth/cryptauth/cryptauth_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace proximity_auth {
+
+class MockCryptAuthClient : public CryptAuthClient {
+ public:
+  MockCryptAuthClient();
+  ~MockCryptAuthClient() override;
+
+  // CryptAuthClient:
+  MOCK_METHOD3(GetMyDevices,
+               void(const cryptauth::GetMyDevicesRequest& request,
+                    const GetMyDevicesCallback& callback,
+                    const ErrorCallback& error_callback));
+  MOCK_METHOD3(FindEligibleUnlockDevices,
+               void(const cryptauth::FindEligibleUnlockDevicesRequest& request,
+                    const FindEligibleUnlockDevicesCallback& callback,
+                    const ErrorCallback& error_callback));
+  MOCK_METHOD3(SendDeviceSyncTickle,
+               void(const cryptauth::SendDeviceSyncTickleRequest& request,
+                    const SendDeviceSyncTickleCallback& callback,
+                    const ErrorCallback& error_callback));
+  MOCK_METHOD3(ToggleEasyUnlock,
+               void(const cryptauth::ToggleEasyUnlockRequest& request,
+                    const ToggleEasyUnlockCallback& callback,
+                    const ErrorCallback& error_callback));
+  MOCK_METHOD3(SetupEnrollment,
+               void(const cryptauth::SetupEnrollmentRequest& request,
+                    const SetupEnrollmentCallback& callback,
+                    const ErrorCallback& error_callback));
+  MOCK_METHOD3(FinishEnrollment,
+               void(const cryptauth::FinishEnrollmentRequest& request,
+                    const FinishEnrollmentCallback& callback,
+                    const ErrorCallback& error_callback));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClient);
+};
+
+class MockCryptAuthClientFactory : public CryptAuthClientFactory {
+ public:
+  class Observer {
+   public:
+    // Called with the new instance when it is requested from the factory,
+    // allowing expectations to be set. Ownership of |client| will be taken by
+    // the caller of CreateInstance().
+    virtual void OnCryptAuthClientCreated(MockCryptAuthClient* client) = 0;
+  };
+
+  // If |is_strict| is true, then StrictMocks will be created. Otherwise,
+  // NiceMocks will be created.
+  explicit MockCryptAuthClientFactory(bool is_strict);
+  ~MockCryptAuthClientFactory() override;
+
+  // CryptAuthClientFactory:
+  scoped_ptr<CryptAuthClient> CreateInstance() override;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ private:
+  // Whether to create StrictMocks or NiceMocks.
+  bool is_strict_;
+
+  // Observers of the factory.
+  ObserverList<Observer> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockCryptAuthClientFactory);
+};
+
+}  // namespace proximity_auth
+
+#endif  // COMPONENTS_PROXIMITY_AUTH_MOCK_CRYPTAUTH_CLIENT_H
diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc
index 2da5a986..1191a96 100644
--- a/components/signin/core/browser/account_tracker_service.cc
+++ b/components/signin/core/browser/account_tracker_service.cc
@@ -28,6 +28,9 @@
 const char kAccountEmailPath[] = "email";
 const char kAccountGaiaPath[] = "gaia";
 const char kAccountHostedDomainPath[] = "hd";
+const char kAccountFullNamePath[] = "full_name";
+const char kAccountGivenNamePath[] = "given_name";
+const char kAccountLocalePath[] = "locale";
 
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
 // IsRefreshTokenDeviceIdExperimentEnabled is called from
@@ -167,8 +170,9 @@
 AccountTrackerService::AccountInfo::~AccountInfo() {}
 
 bool AccountTrackerService::AccountInfo::IsValid() {
-  return account_id.empty() || email.empty() || gaia.empty() ||
-         hosted_domain.empty();
+  return !account_id.empty() && !email.empty() && !gaia.empty() &&
+         !hosted_domain.empty() && !full_name.empty() && !given_name.empty() &&
+         !locale.empty();
 }
 
 
@@ -319,7 +323,7 @@
   // ensure the Fetch doesn't occur until after ProfileImpl::OnPrefsLoaded().
   if (state.info.gaia.empty())
 #else
-  if (state.info.IsValid())
+  if (!state.info.IsValid())
 #endif
     StartFetchingUserInfo(account_id);
 
@@ -420,6 +424,10 @@
       state.info.hosted_domain = kNoHostedDomainFound;
     }
 
+    user_info->GetString("name", &state.info.full_name);
+    user_info->GetString("given_name", &state.info.given_name);
+    user_info->GetString("locale", &state.info.locale);
+
     NotifyAccountUpdated(state);
     SaveToPrefs(state);
   }
@@ -470,7 +478,13 @@
           state.info.email = base::UTF16ToUTF8(value);
         if (dict->GetString(kAccountHostedDomainPath, &value))
           state.info.hosted_domain = base::UTF16ToUTF8(value);
-        if (!state.info.IsValid())
+        if (dict->GetString(kAccountFullNamePath, &value))
+          state.info.full_name = base::UTF16ToUTF8(value);
+        if (dict->GetString(kAccountGivenNamePath, &value))
+          state.info.given_name = base::UTF16ToUTF8(value);
+        if (dict->GetString(kAccountLocalePath, &value))
+          state.info.locale = base::UTF16ToUTF8(value);
+        if (state.info.IsValid())
           NotifyAccountUpdated(state);
       }
     }
@@ -501,6 +515,9 @@
   dict->SetString(kAccountEmailPath, state.info.email);
   dict->SetString(kAccountGaiaPath, state.info.gaia);
   dict->SetString(kAccountHostedDomainPath, state.info.hosted_domain);
+  dict->SetString(kAccountFullNamePath, state.info.full_name);
+  dict->SetString(kAccountGivenNamePath, state.info.given_name);
+  dict->SetString(kAccountLocalePath, state.info.locale);
 }
 
 void AccountTrackerService::RemoveFromPrefs(const AccountState& state) {
diff --git a/components/signin/core/browser/account_tracker_service.h b/components/signin/core/browser/account_tracker_service.h
index d037eb2..20249dd 100644
--- a/components/signin/core/browser/account_tracker_service.h
+++ b/components/signin/core/browser/account_tracker_service.h
@@ -49,7 +49,10 @@
     std::string account_id;  // The account ID used by OAuth2TokenService.
     std::string gaia;
     std::string email;
+    std::string full_name;
+    std::string given_name;
     std::string hosted_domain;
+    std::string locale;
     // TODO(rogerta): eventually this structure will include other information
     // about the account, like full name, profile picture URL, etc.
 
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index c5bb7b4..137e8e31 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -22,6 +22,23 @@
 
 namespace {
 
+const std::string kTokenInfoResponseFormat =
+    "{                        \
+      \"id\": \"%s\",         \
+      \"email\": \"%s\",      \
+      \"hd\": \"\",           \
+      \"name\": \"%s\",       \
+      \"given_name\": \"%s\", \
+      \"locale\": \"%s\"      \
+    }";
+
+const std::string kTokenInfoIncompleteResponseFormat =
+    "{                        \
+      \"id\": \"%s\",         \
+      \"email\": \"%s\",      \
+      \"hd\": \"\",           \
+    }";
+
 enum TrackingEventType {
   UPDATED,
   REMOVED,
@@ -35,6 +52,30 @@
   return "gaia-" + account_id;
 }
 
+std::string AccountIdToFullName(const std::string account_id) {
+  return "full-name-" + account_id;
+}
+
+std::string AccountIdToGivenName(const std::string account_id) {
+  return "given-name-" + account_id;
+}
+
+std::string AccountIdToLocale(const std::string account_id) {
+  return "locale-" + account_id;
+}
+
+void CheckAccountDetails(const std::string account_id,
+                         const AccountTrackerService::AccountInfo& info) {
+  EXPECT_EQ(account_id, info.account_id);
+  EXPECT_EQ(AccountIdToGaiaId(account_id), info.gaia);
+  EXPECT_EQ(AccountIdToEmail(account_id), info.email);
+  EXPECT_EQ(AccountTrackerService::kNoHostedDomainFound,
+            info.hosted_domain);
+  EXPECT_EQ(AccountIdToFullName(account_id), info.full_name);
+  EXPECT_EQ(AccountIdToGivenName(account_id), info.given_name);
+  EXPECT_EQ(AccountIdToLocale(account_id), info.locale);
+}
+
 class TrackingEvent {
  public:
   TrackingEvent(TrackingEventType type,
@@ -236,11 +277,23 @@
 
   std::string GenerateValidTokenInfoResponse(const std::string& account_id) {
     return base::StringPrintf(
-        "{\"id\": \"%s\", \"email\": \"%s\", \"hd\": \"\"}",
+        kTokenInfoResponseFormat.c_str(),
+        AccountIdToGaiaId(account_id).c_str(),
+        AccountIdToEmail(account_id).c_str(),
+        AccountIdToFullName(account_id).c_str(),
+        AccountIdToGivenName(account_id).c_str(),
+        AccountIdToLocale(account_id).c_str());
+  }
+
+  std::string GenerateIncompleteTokenInfoResponse(
+      const std::string& account_id) {
+    return base::StringPrintf(
+        kTokenInfoIncompleteResponseFormat.c_str(),
         AccountIdToGaiaId(account_id).c_str(),
         AccountIdToEmail(account_id).c_str());
   }
   void ReturnOAuthUrlFetchSuccess(const std::string& account_id);
+  void ReturnOAuthUrlFetchSuccessIncomplete(const std::string& account_id);
   void ReturnOAuthUrlFetchFailure(const std::string& account_id);
 
   net::TestURLFetcherFactory* test_fetcher_factory() {
@@ -287,6 +340,14 @@
                              GenerateValidTokenInfoResponse(account_id));
 }
 
+void AccountTrackerServiceTest::ReturnOAuthUrlFetchSuccessIncomplete(
+    const std::string& account_id) {
+  IssueAccessToken(account_id);
+  ReturnOAuthUrlFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId,
+                             net::HTTP_OK,
+                             GenerateIncompleteTokenInfoResponse(account_id));
+}
+
 void AccountTrackerServiceTest::ReturnOAuthUrlFetchFailure(
     const std::string& account_id) {
   IssueAccessToken(account_id);
@@ -390,21 +451,9 @@
       account_tracker()->GetAccounts();
 
   EXPECT_EQ(3u, infos.size());
-  EXPECT_EQ("alpha", infos[0].account_id);
-  EXPECT_EQ(AccountIdToGaiaId("alpha"), infos[0].gaia);
-  EXPECT_EQ(AccountIdToEmail("alpha"), infos[0].email);
-  EXPECT_EQ(AccountTrackerService::kNoHostedDomainFound,
-            infos[0].hosted_domain);
-  EXPECT_EQ("beta", infos[1].account_id);
-  EXPECT_EQ(AccountIdToGaiaId("beta"), infos[1].gaia);
-  EXPECT_EQ(AccountIdToEmail("beta"), infos[1].email);
-  EXPECT_EQ(AccountTrackerService::kNoHostedDomainFound,
-            infos[1].hosted_domain);
-  EXPECT_EQ("gamma", infos[2].account_id);
-  EXPECT_EQ(AccountIdToGaiaId("gamma"), infos[2].gaia);
-  EXPECT_EQ(AccountIdToEmail("gamma"), infos[2].email);
-  EXPECT_EQ(AccountTrackerService::kNoHostedDomainFound,
-            infos[2].hosted_domain);
+  CheckAccountDetails("alpha", infos[0]);
+  CheckAccountDetails("beta", infos[1]);
+  CheckAccountDetails("gamma", infos[2]);
 }
 
 TEST_F(AccountTrackerServiceTest, GetAccountInfo_Empty) {
@@ -427,10 +476,7 @@
   ReturnOAuthUrlFetchSuccess("alpha");
   AccountTrackerService::AccountInfo info =
       account_tracker()->GetAccountInfo("alpha");
-  ASSERT_EQ("alpha", info.account_id);
-  ASSERT_EQ(AccountIdToGaiaId("alpha"), info.gaia);
-  ASSERT_EQ(AccountIdToEmail("alpha"), info.email);
-  ASSERT_EQ(AccountTrackerService::kNoHostedDomainFound, info.hosted_domain);
+  CheckAccountDetails("alpha", info);
 }
 
 TEST_F(AccountTrackerServiceTest, GetAccountInfo_TokenAvailable_EnableNetwork) {
@@ -457,10 +503,7 @@
 
   AccountTrackerService::AccountInfo info =
       tracker.GetAccountInfo("alpha");
-  ASSERT_EQ("alpha", info.account_id);
-  ASSERT_EQ(AccountIdToGaiaId("alpha"), info.gaia);
-  ASSERT_EQ(AccountIdToEmail("alpha"), info.email);
-  ASSERT_EQ(AccountTrackerService::kNoHostedDomainFound, info.hosted_domain);
+  CheckAccountDetails("alpha", info);
   tracker.Shutdown();
 }
 
@@ -516,7 +559,7 @@
     tracker.Shutdown();
   }
 
-  // Create a new tracker and make sure it loads the accounts corectly from
+  // Create a new tracker and make sure it loads the accounts correctly from
   // persistence.
   {
     AccountTrackerService tracker;
@@ -529,11 +572,8 @@
     std::vector<AccountTrackerService::AccountInfo> infos =
         tracker.GetAccounts();
     ASSERT_EQ(2u, infos.size());
-    EXPECT_EQ(AccountIdToGaiaId("alpha"), infos[0].gaia);
-    EXPECT_EQ(AccountIdToEmail("alpha"), infos[0].email);
-    EXPECT_EQ("beta", infos[1].account_id);
-    EXPECT_EQ(AccountIdToGaiaId("beta"), infos[1].gaia);
-    EXPECT_EQ(AccountIdToEmail("beta"), infos[1].email);
+    CheckAccountDetails("alpha", infos[0]);
+    CheckAccountDetails("beta", infos[1]);
 
     // Remove account.
     SimulateTokenRevoked("alpha");
@@ -551,9 +591,7 @@
     std::vector<AccountTrackerService::AccountInfo> infos =
         tracker.GetAccounts();
     ASSERT_EQ(1u, infos.size());
-    EXPECT_EQ("beta", infos[0].account_id);
-    EXPECT_EQ(AccountIdToGaiaId("beta"), infos[0].gaia);
-    EXPECT_EQ(AccountIdToEmail("beta"), infos[0].email);
+    CheckAccountDetails("beta", infos[0]);
     tracker.Shutdown();
   }
 }
@@ -575,3 +613,63 @@
   EXPECT_EQ(gaia_id, infos[0].gaia);
   EXPECT_EQ(email, infos[0].email);
 }
+
+TEST_F(AccountTrackerServiceTest, UpgradeToFullAccountInfo) {
+  // Start by simulating an incomplete account info and let it be saved to
+  // prefs.
+  {
+    AccountTrackerService tracker;
+    tracker.Initialize(token_service(), signin_client());
+    tracker.EnableNetworkFetches();
+    SimulateTokenAvailable("incomplete");
+    ReturnOAuthUrlFetchSuccessIncomplete("incomplete");
+    tracker.Shutdown();
+  }
+
+  {
+    AccountTrackerService tracker;
+    tracker.Initialize(token_service(), signin_client());
+
+    // Validate that the loaded AccountInfo from prefs is considered invalid.
+    std::vector<AccountTrackerService::AccountInfo> infos =
+        tracker.GetAccounts();
+    ASSERT_EQ(1u, infos.size());
+    ASSERT_FALSE(infos[0].IsValid());
+
+    // Enable network fetches and simulate the same account getting a refresh
+    // token containing all the info.
+    tracker.EnableNetworkFetches();
+    SimulateTokenAvailable("incomplete");
+    ReturnOAuthUrlFetchSuccess("incomplete");
+
+    // Validate that the account is now considered valid.
+    infos = tracker.GetAccounts();
+    ASSERT_EQ(1u, infos.size());
+    ASSERT_TRUE(infos[0].IsValid());
+
+    tracker.Shutdown();
+  }
+
+  // Reinstantiate a tracker to validate that the AccountInfo saved to prefs is
+  // now the upgraded one, considered valid.
+  {
+    AccountTrackerService tracker;
+    tracker.AddObserver(observer());
+    tracker.Initialize(token_service(), signin_client());
+    ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "incomplete")));
+    // Make sure there are no events in the observer
+    observer()->Clear();
+    // Enabling network fetches shouldn't cause any actual fetch since the
+    // AccountInfos loaded from prefs should be valid.
+    tracker.EnableNetworkFetches();
+
+    std::vector<AccountTrackerService::AccountInfo> infos =
+        tracker.GetAccounts();
+    ASSERT_EQ(1u, infos.size());
+    ASSERT_TRUE(infos[0].IsValid());
+    // Check that no network fetches were made.
+    ASSERT_TRUE(observer()->CheckEvents());
+
+    tracker.Shutdown();
+  }
+}
diff --git a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
new file mode 100644
index 0000000..af82404c
--- /dev/null
+++ b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
@@ -0,0 +1,77 @@
+// 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/callback.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_tree.h"
+
+namespace content {
+
+namespace {
+
+class AXTreeSnapshotWaiter {
+ public:
+  AXTreeSnapshotWaiter() : loop_runner_(new MessageLoopRunner()) {}
+
+  void Wait() {
+    loop_runner_->Run();
+  }
+
+  const ui::AXTreeUpdate& snapshot() const { return snapshot_; }
+
+  void ReceiveSnapshot(const ui::AXTreeUpdate& snapshot) {
+    snapshot_ = snapshot;
+    loop_runner_->Quit();
+  }
+
+ private:
+  ui::AXTreeUpdate snapshot_;
+  scoped_refptr<MessageLoopRunner> loop_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(AXTreeSnapshotWaiter);
+};
+
+}  // namespace
+
+class SnapshotAXTreeBrowserTest : public ContentBrowserTest {
+ public:
+  SnapshotAXTreeBrowserTest() {}
+  ~SnapshotAXTreeBrowserTest() override {}
+};
+
+IN_PROC_BROWSER_TEST_F(SnapshotAXTreeBrowserTest,
+                       SnapshotAccessibilityTreeFromWebContents) {
+  GURL url("data:text/html,<button>Click</button>");
+  NavigateToURL(shell(), url);
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  AXTreeSnapshotWaiter waiter;
+  web_contents->RequestAXTreeSnapshot(
+      base::Bind(&AXTreeSnapshotWaiter::ReceiveSnapshot,
+                 base::Unretained(&waiter)));
+  waiter.Wait();
+
+  // Dump the whole tree if one of the assertions below fails
+  // to aid in debugging why it failed.
+  SCOPED_TRACE(waiter.snapshot().ToString());
+
+  ui::AXTree tree(waiter.snapshot());
+  ui::AXNode* root = tree.root();
+  ASSERT_NE(nullptr, root);
+  ASSERT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, root->data().role);
+  ui::AXNode* group = root->ChildAtIndex(0);
+  ASSERT_EQ(ui::AX_ROLE_GROUP, group->data().role);
+  ui::AXNode* button = group->ChildAtIndex(0);
+  ASSERT_EQ(ui::AX_ROLE_BUTTON, button->data().role);
+}
+
+}  // namespace content
diff --git a/content/browser/android/content_startup_flags.cc b/content/browser/android/content_startup_flags.cc
index e5acab94..e679825 100644
--- a/content/browser/android/content_startup_flags.cc
+++ b/content/browser/android/content_startup_flags.cc
@@ -71,12 +71,6 @@
   if (base::SysInfo::IsLowEndDevice())
     parsed_command_line->AppendSwitch(switches::kInProcessGPU);
 
-  // Web Notifications are only supported on Android JellyBean and beyond.
-  if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_JELLY_BEAN) {
-    parsed_command_line->AppendSwitch(switches::kDisableNotifications);
-  }
-
   parsed_command_line->AppendSwitch(switches::kEnableViewportMeta);
   parsed_command_line->AppendSwitch(
       switches::kMainFrameResizesAreOrientationChanges);
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index a8ce1b0..78e3836 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -15,6 +15,7 @@
 #include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_restrictions.h"
 #include "content/public/browser/browser_thread_delegate.h"
+#include "content/public/browser/content_browser_client.h"
 #include "net/disk_cache/simple/simple_backend_impl.h"
 
 #if defined(OS_ANDROID)
@@ -365,6 +366,15 @@
 }
 
 // static
+void BrowserThread::PostAfterStartupTask(
+    const tracked_objects::Location& from_here,
+    const scoped_refptr<base::TaskRunner>& task_runner,
+    const base::Closure& task) {
+  GetContentClient()->browser()->PostAfterStartupTask(from_here, task_runner,
+                                                      task);
+}
+
+// static
 base::SequencedWorkerPool* BrowserThread::GetBlockingPool() {
   return g_globals.Get().blocking_pool.get();
 }
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc
new file mode 100644
index 0000000..03fb0e2
--- /dev/null
+++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/browser_devtools_agent_host.h"
+
+#include "base/bind.h"
+#include "content/browser/devtools/protocol/devtools_protocol_handler.h"
+#include "content/browser/devtools/protocol/system_info_handler.h"
+#include "content/browser/devtools/protocol/tethering_handler.h"
+#include "content/browser/devtools/protocol/tracing_handler.h"
+
+namespace content {
+
+scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::CreateForBrowser(
+    scoped_refptr<base::MessageLoopProxy> tethering_message_loop,
+    const CreateServerSocketCallback& socket_callback) {
+  return new BrowserDevToolsAgentHost(tethering_message_loop, socket_callback);
+}
+
+BrowserDevToolsAgentHost::BrowserDevToolsAgentHost(
+    scoped_refptr<base::MessageLoopProxy> tethering_message_loop,
+    const CreateServerSocketCallback& socket_callback)
+    : system_info_handler_(new devtools::system_info::SystemInfoHandler()),
+      tethering_handler_(new devtools::tethering::TetheringHandler(
+          socket_callback, tethering_message_loop)),
+      tracing_handler_(new devtools::tracing::TracingHandler(
+          devtools::tracing::TracingHandler::Browser)) {
+  set_handle_all_protocol_commands();
+  DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
+  dispatcher->SetSystemInfoHandler(system_info_handler_.get());
+  dispatcher->SetTetheringHandler(tethering_handler_.get());
+  dispatcher->SetTracingHandler(tracing_handler_.get());
+}
+
+BrowserDevToolsAgentHost::~BrowserDevToolsAgentHost() {
+}
+
+void BrowserDevToolsAgentHost::Attach() {
+}
+
+void BrowserDevToolsAgentHost::Detach() {
+}
+
+DevToolsAgentHost::Type BrowserDevToolsAgentHost::GetType() {
+  return TYPE_BROWSER;
+}
+
+std::string BrowserDevToolsAgentHost::GetTitle() {
+  return "";
+}
+
+GURL BrowserDevToolsAgentHost::GetURL() {
+  return GURL();
+}
+
+bool BrowserDevToolsAgentHost::Activate() {
+  return false;
+}
+
+bool BrowserDevToolsAgentHost::Close() {
+  return false;
+}
+
+}  // content
diff --git a/content/browser/devtools/browser_devtools_agent_host.h b/content/browser/devtools/browser_devtools_agent_host.h
new file mode 100644
index 0000000..b4fbb7d
--- /dev/null
+++ b/content/browser/devtools/browser_devtools_agent_host.h
@@ -0,0 +1,44 @@
+// 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 CONTENT_BROWSER_DEVTOOLS_BROWSER_DEVTOOLS_AGENT_HOST_H_
+#define CONTENT_BROWSER_DEVTOOLS_BROWSER_DEVTOOLS_AGENT_HOST_H_
+
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+
+namespace content {
+
+namespace devtools {
+namespace system_info { class SystemInfoHandler; }
+namespace tethering { class TetheringHandler; }
+namespace tracing { class TracingHandler; }
+}  // namespace devtools
+
+class BrowserDevToolsAgentHost : public DevToolsAgentHostImpl {
+ private:
+  friend class DevToolsAgentHost;
+  BrowserDevToolsAgentHost(
+      scoped_refptr<base::MessageLoopProxy> tethering_message_loop,
+      const CreateServerSocketCallback& socket_callback);
+  ~BrowserDevToolsAgentHost() override;
+
+  // DevToolsAgentHostImpl implementation.
+  void Attach() override;
+  void Detach() override;
+
+  // DevToolsAgentHost implementation.
+  Type GetType() override;
+  std::string GetTitle() override;
+  GURL GetURL() override;
+  bool Activate() override;
+  bool Close() override;
+
+  scoped_ptr<devtools::system_info::SystemInfoHandler> system_info_handler_;
+  scoped_ptr<devtools::tethering::TetheringHandler> tethering_handler_;
+  scoped_ptr<devtools::tracing::TracingHandler> tracing_handler_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_BROWSER_DEVTOOLS_AGENT_HOST_H_
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc
index efd76cb..3eb6acec 100644
--- a/content/browser/devtools/devtools_agent_host_impl.cc
+++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -35,6 +35,16 @@
 }  // namespace
 
 // static
+std::string DevToolsAgentHost::GetProtocolVersion() {
+  return std::string(devtools::kProtocolVersion);
+}
+
+// static
+bool DevToolsAgentHost::IsSupportedProtocolVersion(const std::string& version) {
+  return devtools::IsSupportedProtocolVersion(version);
+}
+
+// static
 DevToolsAgentHost::List DevToolsAgentHost::GetOrCreateAll() {
   List result;
   SharedWorkerDevToolsAgentHost::List shared_list;
@@ -71,7 +81,8 @@
           base::Bind(&DevToolsAgentHostImpl::SendMessageToClient,
                      base::Unretained(this)))),
       id_(base::GenerateGUID()),
-      client_(NULL) {
+      client_(NULL),
+      handle_all_commands_(false) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   g_instances.Get()[id_] = this;
 }
@@ -144,10 +155,6 @@
 void DevToolsAgentHostImpl::ConnectWebContents(WebContents* wc) {
 }
 
-bool DevToolsAgentHostImpl::IsWorker() const {
-  return false;
-}
-
 void DevToolsAgentHostImpl::HostClosed() {
   if (!client_)
     return;
@@ -244,7 +251,10 @@
     }
   }
 
-  return protocol_handler_->HandleOptionalCommand(command.Pass());
+  if (!handle_all_commands_)
+    return protocol_handler_->HandleOptionalCommand(command.Pass());
+  protocol_handler_->HandleCommand(command.Pass());
+  return true;
 }
 
 }  // namespace content
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h
index 07112c3..698bfe90 100644
--- a/content/browser/devtools/devtools_agent_host_impl.h
+++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -45,7 +45,6 @@
   WebContents* GetWebContents() override;
   void DisconnectWebContents() override;
   void ConnectWebContents(WebContents* wc) override;
-  bool IsWorker() const override;
 
  protected:
   DevToolsAgentHostImpl();
@@ -53,6 +52,7 @@
 
   scoped_ptr<DevToolsProtocolHandler> protocol_handler_;
 
+  void set_handle_all_protocol_commands() { handle_all_commands_ = true; }
   void HostClosed();
   void SendMessageToClient(const std::string& message);
   static void NotifyCallbacks(DevToolsAgentHostImpl* agent_host, bool attached);
@@ -62,6 +62,7 @@
 
   const std::string id_;
   DevToolsAgentHostClient* client_;
+  bool handle_all_commands_;
 };
 
 }  // namespace content
diff --git a/content/browser/devtools/devtools_http_handler_impl.cc b/content/browser/devtools/devtools_http_handler_impl.cc
index fb25825d..4cae4481 100644
--- a/content/browser/devtools/devtools_http_handler_impl.cc
+++ b/content/browser/devtools/devtools_http_handler_impl.cc
@@ -13,14 +13,11 @@
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread.h"
 #include "base/values.h"
 #include "content/browser/devtools/devtools_manager.h"
-#include "content/browser/devtools/protocol/devtools_protocol_handler.h"
-#include "content/browser/devtools/protocol/system_info_handler.h"
-#include "content/browser/devtools/protocol/tethering_handler.h"
-#include "content/browser/devtools/protocol/tracing_handler.h"
-#include "content/common/devtools_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_http_handler.h"
@@ -71,7 +68,6 @@
 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
 const int32 kSendBufferSizeForDevTools = 256 * 1024 * 1024;  // 256Mb
 
-class BrowserTarget;
 class DevToolsAgentHostClientImpl;
 class ServerWrapper;
 
@@ -148,8 +144,6 @@
   ServerSocketFactory* socket_factory_;
   typedef std::map<std::string, DevToolsTarget*> TargetMap;
   TargetMap target_map_;
-  typedef std::map<int, BrowserTarget*> BrowserTargets;
-  BrowserTargets browser_targets_;
   base::WeakPtrFactory<DevToolsHttpHandlerImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl);
@@ -346,7 +340,7 @@
   DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
                               ServerWrapper* server_wrapper,
                               int connection_id,
-                              DevToolsAgentHost* agent_host)
+                              scoped_refptr<DevToolsAgentHost> agent_host)
       : message_loop_(message_loop),
         server_wrapper_(server_wrapper),
         connection_id_(connection_id),
@@ -363,13 +357,12 @@
                        bool replaced_with_another_client) override {
     DCHECK(agent_host == agent_host_.get());
 
-    base::Callback<void(const std::string&)> raw_message_callback(
-        base::Bind(&DevToolsAgentHostClientImpl::DispatchProtocolMessage,
-                   base::Unretained(this), base::Unretained(agent_host)));
-    devtools::inspector::Client inspector(raw_message_callback);
-    inspector.Detached(devtools::inspector::DetachedParams::Create()
-       ->set_reason(replaced_with_another_client ?
-            "replaced_with_devtools" : "target_closed"));
+    std::string message = base::StringPrintf(
+        "{ \"method\": \"Inspector.detached\", "
+        "\"params\": { \"reason\": \"%s\"} }",
+        replaced_with_another_client ?
+            "replaced_with_devtools" : "target_closed");
+    DispatchProtocolMessage(agent_host, message);
 
     agent_host_ = nullptr;
     message_loop_->PostTask(
@@ -407,59 +400,6 @@
   return target1->GetLastActivityTime() > target2->GetLastActivityTime();
 }
 
-// BrowserTarget -------------------------------------------------------------
-
-class BrowserTarget {
- public:
-  BrowserTarget(base::MessageLoop* message_loop,
-                ServerWrapper* server_wrapper,
-                DevToolsHttpHandler::ServerSocketFactory* socket_factory,
-                int connection_id)
-      : message_loop_(message_loop),
-        server_wrapper_(server_wrapper),
-        connection_id_(connection_id),
-        system_info_handler_(new devtools::system_info::SystemInfoHandler()),
-        tethering_handler_(new devtools::tethering::TetheringHandler(
-            socket_factory, message_loop->message_loop_proxy())),
-        tracing_handler_(new devtools::tracing::TracingHandler(
-            devtools::tracing::TracingHandler::Browser)),
-        protocol_handler_(new DevToolsProtocolHandler(
-            base::Bind(&BrowserTarget::Respond, base::Unretained(this)))) {
-    DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
-    dispatcher->SetSystemInfoHandler(system_info_handler_.get());
-    dispatcher->SetTetheringHandler(tethering_handler_.get());
-    dispatcher->SetTracingHandler(tracing_handler_.get());
-  }
-
-  void HandleMessage(const std::string& message) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    std::string error_response;
-    scoped_ptr<base::DictionaryValue> command =
-        protocol_handler_->ParseCommand(message);
-    if (command)
-      protocol_handler_->HandleCommand(command.Pass());
-  }
-
-  void Respond(const std::string& message) {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    message_loop_->PostTask(
-        FROM_HERE,
-        base::Bind(&ServerWrapper::SendOverWebSocket,
-                   base::Unretained(server_wrapper_),
-                   connection_id_,
-                   message));
-  }
-
- private:
-  base::MessageLoop* const message_loop_;
-  ServerWrapper* const server_wrapper_;
-  const int connection_id_;
-  scoped_ptr<devtools::system_info::SystemInfoHandler> system_info_handler_;
-  scoped_ptr<devtools::tethering::TetheringHandler> tethering_handler_;
-  scoped_ptr<devtools::tracing::TracingHandler> tracing_handler_;
-  scoped_ptr<DevToolsProtocolHandler> protocol_handler_;
-};
-
 }  // namespace
 
 // DevToolsHttpHandler -------------------------------------------------------
@@ -467,7 +407,7 @@
 // static
 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
     const std::string& version) {
-  return devtools::IsSupportedProtocolVersion(version);
+  return DevToolsAgentHost::IsSupportedProtocolVersion(version);
 }
 
 // static
@@ -511,7 +451,6 @@
 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
   TerminateOnUI(thread_, server_wrapper_, socket_factory_);
   STLDeleteValues(&target_map_);
-  STLDeleteValues(&browser_targets_);
   STLDeleteValues(&connection_to_client_);
 }
 
@@ -718,7 +657,8 @@
 
   if (command == "version") {
     base::DictionaryValue version;
-    version.SetString("Protocol-Version", devtools::kProtocolVersion);
+    version.SetString("Protocol-Version",
+        DevToolsAgentHost::GetProtocolVersion().c_str());
     version.SetString("WebKit-Version", GetWebKitVersion());
     version.SetString("Browser", GetContentClient()->GetProduct());
     version.SetString("User-Agent", GetContentClient()->GetUserAgent());
@@ -880,10 +820,13 @@
   std::string browser_prefix = "/devtools/browser";
   size_t browser_pos = request.path.find(browser_prefix);
   if (browser_pos == 0) {
-    browser_targets_[connection_id] = new BrowserTarget(thread_->message_loop(),
-                                                        server_wrapper_,
-                                                        socket_factory_,
-                                                        connection_id);
+    scoped_refptr<DevToolsAgentHost> browser_agent =
+        DevToolsAgentHost::CreateForBrowser(
+            thread_->message_loop_proxy(),
+            base::Bind(&ServerSocketFactory::CreateForTethering,
+                       base::Unretained(socket_factory_)));
+    connection_to_client_[connection_id] = new DevToolsAgentHostClientImpl(
+        thread_->message_loop(), server_wrapper_, connection_id, browser_agent);
     AcceptWebSocket(connection_id, request);
     return;
   }
@@ -910,7 +853,7 @@
   }
 
   DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
-      thread_->message_loop(), server_wrapper_, connection_id, agent.get());
+      thread_->message_loop(), server_wrapper_, connection_id, agent);
   connection_to_client_[connection_id] = client_host;
 
   AcceptWebSocket(connection_id, request);
@@ -919,12 +862,6 @@
 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
     int connection_id,
     const std::string& data) {
-  BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
-  if (browser_it != browser_targets_.end()) {
-    browser_it->second->HandleMessage(data);
-    return;
-  }
-
   ConnectionToClientMap::iterator it =
       connection_to_client_.find(connection_id);
   if (it != connection_to_client_.end())
@@ -932,13 +869,6 @@
 }
 
 void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
-  BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
-  if (browser_it != browser_targets_.end()) {
-    delete browser_it->second;
-    browser_targets_.erase(connection_id);
-    return;
-  }
-
   ConnectionToClientMap::iterator it =
       connection_to_client_.find(connection_id);
   if (it != connection_to_client_.end()) {
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 129b125b..55e9826 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -4,26 +4,196 @@
 
 #include "content/browser/devtools/protocol/network_handler.h"
 
+#include "base/containers/hash_tables.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/common/content_client.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
 
 namespace content {
 namespace devtools {
 namespace network {
 
+using CookieListCallback = net::CookieStore::GetCookieListCallback;
+
+namespace {
+
+net::URLRequestContext* GetRequestContextOnIO(
+    ResourceContext* resource_context,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  net::URLRequestContext* context =
+      GetContentClient()->browser()->OverrideRequestContextForURL(
+          url, resource_context);
+  if (!context)
+    context = context_getter->GetURLRequestContext();
+  return context;
+}
+
+void GotCookiesForURLOnIO(
+    const CookieListCallback& callback,
+    const net::CookieList& cookie_list) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  BrowserThread::PostTask(
+      BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(callback, cookie_list));
+}
+
+void GetCookiesForURLOnIO(
+    ResourceContext* resource_context,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& url,
+    const CookieListCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  net::URLRequestContext* request_context =
+      GetRequestContextOnIO(resource_context, context_getter, url);
+  request_context->cookie_store()->GetAllCookiesForURLAsync(
+      url, base::Bind(&GotCookiesForURLOnIO, callback));
+}
+
+void GetCookiesForURLOnUI(
+    ResourceContext* resource_context,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& url,
+    const CookieListCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&GetCookiesForURLOnIO,
+                 base::Unretained(resource_context),
+                 base::Unretained(context_getter),
+                 url,
+                 callback));
+}
+
+void DeletedCookieOnIO(const base::Closure& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  BrowserThread::PostTask(
+      BrowserThread::UI,
+      FROM_HERE,
+      callback);
+}
+
+void DeleteCookieOnIO(
+    ResourceContext* resource_context,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& url,
+    const std::string& cookie_name,
+    const base::Closure& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  net::URLRequestContext* request_context =
+      GetRequestContextOnIO(resource_context, context_getter, url);
+  request_context->cookie_store()->DeleteCookieAsync(
+      url, cookie_name, base::Bind(&DeletedCookieOnIO, callback));
+}
+
+void DeleteCookieOnUI(
+    ResourceContext* resource_context,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& url,
+    const std::string& cookie_name,
+    const base::Closure& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&DeleteCookieOnIO,
+                 base::Unretained(resource_context),
+                 base::Unretained(context_getter),
+                 url,
+                 cookie_name,
+                 callback));
+}
+
+class GetCookiesCommand {
+ public:
+  explicit GetCookiesCommand(
+      RenderFrameHostImpl* frame_host,
+      const CookieListCallback& callback)
+      : callback_(callback),
+        request_count_(0) {
+    CookieListCallback got_cookies_callback = base::Bind(
+        &GetCookiesCommand::GotCookiesForURL, base::Unretained(this));
+    BrowserContext* browser_context =
+        frame_host->GetSiteInstance()->GetBrowserContext();
+
+    std::queue<FrameTreeNode*> queue;
+    queue.push(frame_host->frame_tree_node());
+    while (!queue.empty()) {
+      FrameTreeNode* node = queue.front();
+      queue.pop();
+
+      // Only traverse nodes with the same local root.
+      if (node->current_frame_host()->IsCrossProcessSubframe())
+        continue;
+      int process_id = node->current_frame_host()->GetProcess()->GetID();
+      ++request_count_;
+      GetCookiesForURLOnUI(
+          browser_context->GetResourceContext(),
+          browser_context->GetRequestContextForRenderProcess(process_id),
+          node->current_url(),
+          got_cookies_callback);
+
+      for (size_t i = 0; i < node->child_count(); ++i)
+        queue.push(node->child_at(i));
+    }
+  }
+
+ private:
+  void GotCookiesForURL(const net::CookieList& cookie_list) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    for (const net::CanonicalCookie& cookie : cookie_list) {
+      std::string key = base::StringPrintf(
+          "%s::%s::%s::%d",
+          cookie.Name().c_str(),
+          cookie.Domain().c_str(),
+          cookie.Path().c_str(),
+          cookie.IsSecure());
+      cookies_[key] = cookie;
+    }
+    --request_count_;
+    if (!request_count_) {
+      net::CookieList list;
+      list.reserve(cookies_.size());
+      for (const auto& pair : cookies_)
+        list.push_back(pair.second);
+      callback_.Run(list);
+      delete this;
+    }
+  }
+
+  CookieListCallback callback_;
+  int request_count_;
+  base::hash_map<std::string, net::CanonicalCookie> cookies_;
+};
+
+}  // namespace
+
 typedef DevToolsProtocolClient::Response Response;
 
-NetworkHandler::NetworkHandler() : host_(nullptr) {
+NetworkHandler::NetworkHandler() : host_(nullptr), weak_factory_(this) {
 }
 
 NetworkHandler::~NetworkHandler() {
 }
 
-void NetworkHandler::SetRenderFrameHost(RenderFrameHost* host) {
+void NetworkHandler::SetRenderFrameHost(RenderFrameHostImpl* host) {
   host_ = host;
 }
 
-void NetworkHandler::SetClient(scoped_ptr<DevToolsProtocolClient> client) {
+void NetworkHandler::SetClient(scoped_ptr<Client> client) {
+  client_.swap(client);
 }
 
 Response NetworkHandler::ClearBrowserCache() {
@@ -39,15 +209,63 @@
 }
 
 Response NetworkHandler::GetCookies(DevToolsCommandId command_id) {
+  if (!host_)
+    return Response::InternalError("Could not connect to view");
+  new GetCookiesCommand(
+      host_,
+      base::Bind(&NetworkHandler::SendGetCookiesResponse,
+                 weak_factory_.GetWeakPtr(),
+                 command_id));
   return Response::OK();
 }
 
-Response NetworkHandler::DeleteCookie(DevToolsCommandId command_id,
-                                      const std::string& cookie_name,
-                                      const std::string& url) {
+void NetworkHandler::SendGetCookiesResponse(
+    DevToolsCommandId command_id,
+    const net::CookieList& cookie_list) {
+  std::vector<scoped_refptr<Cookie>> cookies;
+  for (size_t i = 0; i < cookie_list.size(); ++i) {
+    const net::CanonicalCookie& cookie = cookie_list[i];
+    cookies.push_back(Cookie::Create()
+         ->set_name(cookie.Name())
+         ->set_value(cookie.Value())
+         ->set_domain(cookie.Domain())
+         ->set_path(cookie.Path())
+         ->set_expires(cookie.ExpiryDate().ToDoubleT() * 1000)
+         ->set_size(cookie.Name().length() + cookie.Value().length())
+         ->set_http_only(cookie.IsHttpOnly())
+         ->set_secure(cookie.IsSecure())
+         ->set_session(!cookie.IsPersistent()));
+  }
+  client_->SendGetCookiesResponse(command_id,
+      GetCookiesResponse::Create()->set_cookies(cookies));
+}
+
+Response NetworkHandler::DeleteCookie(
+    DevToolsCommandId command_id,
+    const std::string& cookie_name,
+    const std::string& url) {
+  if (!host_)
+    return Response::InternalError("Could not connect to view");
+  BrowserContext* browser_context =
+      host_->GetSiteInstance()->GetBrowserContext();
+  int process_id = host_->GetProcess()->GetID();
+  DeleteCookieOnUI(
+      browser_context->GetResourceContext(),
+      browser_context->GetRequestContextForRenderProcess(process_id),
+      GURL(url),
+      cookie_name,
+      base::Bind(&NetworkHandler::SendDeleteCookieResponse,
+                 weak_factory_.GetWeakPtr(),
+                 command_id));
   return Response::OK();
 }
 
+void NetworkHandler::SendDeleteCookieResponse(DevToolsCommandId command_id) {
+  client_->SendDeleteCookieResponse(command_id,
+      DeleteCookieResponse::Create());
+}
+
+
 Response NetworkHandler::CanEmulateNetworkConditions(bool* result) {
   *result = false;
   return Response::OK();
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index b9a8a9d6..b54a0cb 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -5,11 +5,13 @@
 #ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_NETWORK_HANDLER_H_
 #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_NETWORK_HANDLER_H_
 
+#include "base/memory/weak_ptr.h"
 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
+#include "net/cookies/canonical_cookie.h"
 
 namespace content {
 
-class RenderFrameHost;
+class RenderFrameHostImpl;
 
 namespace devtools {
 namespace network {
@@ -21,8 +23,8 @@
   NetworkHandler();
   virtual ~NetworkHandler();
 
-  void SetRenderFrameHost(RenderFrameHost* host);
-  void SetClient(scoped_ptr<DevToolsProtocolClient> client);
+  void SetRenderFrameHost(RenderFrameHostImpl* host);
+  void SetClient(scoped_ptr<Client> client);
 
   Response ClearBrowserCache();
   Response ClearBrowserCookies();
@@ -38,7 +40,14 @@
                                     double upload_throughput);
 
  private:
-  RenderFrameHost* host_;
+  void SendGetCookiesResponse(
+      DevToolsCommandId command_id,
+      const net::CookieList& cookie_list);
+  void SendDeleteCookieResponse(DevToolsCommandId command_id);
+
+  RenderFrameHostImpl* host_;
+  scoped_ptr<Client> client_;
+  base::WeakPtrFactory<NetworkHandler> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkHandler);
 };
diff --git a/content/browser/devtools/protocol/tethering_handler.cc b/content/browser/devtools/protocol/tethering_handler.cc
index efdf0d14..291a132 100644
--- a/content/browser/devtools/protocol/tethering_handler.cc
+++ b/content/browser/devtools/protocol/tethering_handler.cc
@@ -27,6 +27,8 @@
 const int kMaxTetheringPort = 32767;
 
 using Response = DevToolsProtocolClient::Response;
+using CreateServerSocketCallback =
+    base::Callback<scoped_ptr<net::ServerSocket>(std::string*)>;
 
 class SocketPump {
  public:
@@ -36,9 +38,9 @@
         pending_destruction_(false) {
   }
 
-  std::string Init(DevToolsHttpHandler::ServerSocketFactory* socket_factory) {
+  std::string Init(const CreateServerSocketCallback& socket_callback) {
     std::string channel_name;
-    server_socket_ = socket_factory->CreateForTethering(&channel_name);
+    server_socket_ = socket_callback.Run(&channel_name);
     if (!server_socket_.get() || channel_name.empty())
       SelfDestruct();
 
@@ -157,9 +159,9 @@
   typedef base::Callback<void(uint16, const std::string&)> AcceptedCallback;
 
   BoundSocket(AcceptedCallback accepted_callback,
-              DevToolsHttpHandler::ServerSocketFactory* socket_factory)
+              const CreateServerSocketCallback& socket_callback)
       : accepted_callback_(accepted_callback),
-        socket_factory_(socket_factory),
+        socket_callback_(socket_callback),
         socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())),
         port_(0) {
   }
@@ -213,13 +215,13 @@
       return;
 
     SocketPump* pump = new SocketPump(accept_socket_.release());
-    std::string name = pump->Init(socket_factory_);
+    std::string name = pump->Init(socket_callback_);
     if (!name.empty())
       accepted_callback_.Run(port_, name);
   }
 
   AcceptedCallback accepted_callback_;
-  DevToolsHttpHandler::ServerSocketFactory* socket_factory_;
+  CreateServerSocketCallback socket_callback_;
   scoped_ptr<net::ServerSocket> socket_;
   scoped_ptr<net::StreamSocket> accept_socket_;
   uint16 port_;
@@ -233,7 +235,7 @@
  public:
   TetheringImpl(
       base::WeakPtr<TetheringHandler> handler,
-      DevToolsHttpHandler::ServerSocketFactory* socket_factory);
+      const CreateServerSocketCallback& socket_callback);
   ~TetheringImpl();
 
   void Bind(DevToolsCommandId command_id, uint16 port);
@@ -245,7 +247,7 @@
                          const std::string& message);
 
   base::WeakPtr<TetheringHandler> handler_;
-  DevToolsHttpHandler::ServerSocketFactory* socket_factory_;
+  CreateServerSocketCallback socket_callback_;
 
   typedef std::map<uint16, BoundSocket*> BoundSockets;
   BoundSockets bound_sockets_;
@@ -253,9 +255,9 @@
 
 TetheringHandler::TetheringImpl::TetheringImpl(
     base::WeakPtr<TetheringHandler> handler,
-    DevToolsHttpHandler::ServerSocketFactory* socket_factory)
+    const CreateServerSocketCallback& socket_callback)
     : handler_(handler),
-      socket_factory_(socket_factory) {
+      socket_callback_(socket_callback) {
 }
 
 TetheringHandler::TetheringImpl::~TetheringImpl() {
@@ -272,7 +274,7 @@
   BoundSocket::AcceptedCallback callback = base::Bind(
       &TetheringHandler::TetheringImpl::Accepted, base::Unretained(this));
   scoped_ptr<BoundSocket> bound_socket(
-      new BoundSocket(callback, socket_factory_));
+      new BoundSocket(callback, socket_callback_));
   if (!bound_socket->Listen(port)) {
     SendInternalError(command_id, "Could not bind port");
     return;
@@ -327,9 +329,9 @@
 TetheringHandler::TetheringImpl* TetheringHandler::impl_ = nullptr;
 
 TetheringHandler::TetheringHandler(
-    DevToolsHttpHandler::ServerSocketFactory* socket_factory,
+    const CreateServerSocketCallback& socket_callback,
     scoped_refptr<base::MessageLoopProxy> message_loop_proxy)
-    : socket_factory_(socket_factory),
+    : socket_callback_(socket_callback),
       message_loop_proxy_(message_loop_proxy),
       is_active_(false),
       weak_factory_(this) {
@@ -357,7 +359,7 @@
   if (impl_)
     return false;
   is_active_ = true;
-  impl_ = new TetheringImpl(weak_factory_.GetWeakPtr(), socket_factory_);
+  impl_ = new TetheringImpl(weak_factory_.GetWeakPtr(), socket_callback_);
   return true;
 }
 
diff --git a/content/browser/devtools/protocol/tethering_handler.h b/content/browser/devtools/protocol/tethering_handler.h
index ac6afc8..6be8a0a 100644
--- a/content/browser/devtools/protocol/tethering_handler.h
+++ b/content/browser/devtools/protocol/tethering_handler.h
@@ -18,8 +18,10 @@
 class TetheringHandler {
  public:
   using Response = DevToolsProtocolClient::Response;
+  using CreateServerSocketCallback =
+      base::Callback<scoped_ptr<net::ServerSocket>(std::string*)>;
 
-  TetheringHandler(DevToolsHttpHandler::ServerSocketFactory* delegate,
+  TetheringHandler(const CreateServerSocketCallback& socket_callback,
                    scoped_refptr<base::MessageLoopProxy> message_loop_proxy);
   ~TetheringHandler();
 
@@ -40,7 +42,7 @@
                          const std::string& message);
 
   scoped_ptr<Client> client_;
-  DevToolsHttpHandler::ServerSocketFactory* socket_factory_;
+  CreateServerSocketCallback socket_callback_;
   scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
   bool is_active_;
   base::WeakPtrFactory<TetheringHandler> weak_factory_;
diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc
index 204435a..fcf8dd7 100644
--- a/content/browser/devtools/worker_devtools_agent_host.cc
+++ b/content/browser/devtools/worker_devtools_agent_host.cc
@@ -11,10 +11,6 @@
 
 namespace content {
 
-bool WorkerDevToolsAgentHost::IsWorker() const {
-  return true;
-}
-
 BrowserContext* WorkerDevToolsAgentHost::GetBrowserContext() {
   RenderProcessHost* rph = RenderProcessHost::FromID(worker_id_.first);
   return rph ? rph->GetBrowserContext() : nullptr;
diff --git a/content/browser/devtools/worker_devtools_agent_host.h b/content/browser/devtools/worker_devtools_agent_host.h
index ae5e39fc0..7bd5128 100644
--- a/content/browser/devtools/worker_devtools_agent_host.h
+++ b/content/browser/devtools/worker_devtools_agent_host.h
@@ -19,7 +19,6 @@
   typedef std::pair<int, int> WorkerId;
 
   // DevToolsAgentHost override.
-  bool IsWorker() const override;
   BrowserContext* GetBrowserContext() override;
 
   // IPCDevToolsAgentHost implementation.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index d4cc6f5..6d89910 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -64,6 +64,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "ui/accessibility/ax_tree.h"
+#include "ui/accessibility/ax_tree_update.h"
 #include "url/gurl.h"
 
 #if defined(OS_MACOSX)
@@ -381,6 +382,8 @@
                         OnAccessibilityLocationChanges)
     IPC_MESSAGE_HANDLER(AccessibilityHostMsg_FindInPageResult,
                         OnAccessibilityFindInPageResult)
+    IPC_MESSAGE_HANDLER(AccessibilityHostMsg_SnapshotResponse,
+                        OnAccessibilitySnapshotResponse)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ToggleFullscreen, OnToggleFullscreen)
     // The following message is synthetic and doesn't come from RenderFrame, but
     // from RenderProcessHost.
@@ -1069,6 +1072,12 @@
   SetRenderFrameCreated(false);
   InvalidateMojoConnection();
 
+  // Execute any pending AX tree snapshot callbacks with an empty response,
+  // since we're never going to get a response from this renderer.
+  for (const auto& iter : ax_tree_snapshot_callbacks_)
+    iter.second.Run(ui::AXTreeUpdate());
+  ax_tree_snapshot_callbacks_.clear();
+
   if (frame_tree_node_->IsMainFrame()) {
     // RenderViewHost/RenderWidgetHost needs to reset some stuff.
     render_view_host_->RendererExited(
@@ -1078,6 +1087,10 @@
         render_view_host_, static_cast<base::TerminationStatus>(status),
         exit_code);
   }
+
+  // Note: don't add any more code at this point in the function because
+  // |this| may be deleted. Any additional cleanup should happen before
+  // the last block of code here.
 }
 
 void RenderFrameHostImpl::OnSwappedOut() {
@@ -1392,6 +1405,18 @@
   }
 }
 
+void RenderFrameHostImpl::OnAccessibilitySnapshotResponse(
+    int callback_id,
+    const ui::AXTreeUpdate& snapshot) {
+  const auto& it = ax_tree_snapshot_callbacks_.find(callback_id);
+  if (it != ax_tree_snapshot_callbacks_.end()) {
+    it->second.Run(snapshot);
+    ax_tree_snapshot_callbacks_.erase(it);
+  } else {
+    NOTREACHED() << "Received AX tree snapshot response for unknown id";
+  }
+}
+
 void RenderFrameHostImpl::OnToggleFullscreen(bool enter_fullscreen) {
   if (enter_fullscreen)
     delegate_->EnterFullscreenMode(GetLastCommittedURL().GetOrigin());
@@ -1805,6 +1830,14 @@
   Send(new FrameMsg_SetAccessibilityMode(routing_id_, mode));
 }
 
+void RenderFrameHostImpl::RequestAXTreeSnapshot(
+    AXTreeSnapshotCallback callback) {
+  static int next_id = 1;
+  int callback_id = next_id++;
+  Send(new AccessibilityMsg_SnapshotTree(routing_id_, callback_id));
+  ax_tree_snapshot_callbacks_.insert(std::make_pair(callback_id, callback));
+}
+
 void RenderFrameHostImpl::SetAccessibilityCallbackForTesting(
     const base::Callback<void(ui::AXEvent, int)>& callback) {
   accessibility_testing_callback_ = callback;
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 283be24..9ac9b8d 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -91,6 +91,9 @@
     : public RenderFrameHost,
       public BrowserAccessibilityDelegate {
  public:
+  typedef base::Callback<void(const ui::AXTreeUpdate&)>
+      AXTreeSnapshotCallback;
+
   // Keeps track of the state of the RenderFrameHostImpl, particularly with
   // respect to swap out.
   enum RenderFrameHostImplState {
@@ -366,6 +369,10 @@
   // Send a message to the renderer process to change the accessibility mode.
   void SetAccessibilityMode(AccessibilityMode AccessibilityMode);
 
+  // Request a one-time snapshot of the accessibility tree without changing
+  // the accessibility mode.
+  void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback);
+
   // Turn on accessibility testing. The given callback will be run
   // every time an accessibility notification is received from the
   // renderer process, and the accessibility tree it sent can be
@@ -511,6 +518,8 @@
       const std::vector<AccessibilityHostMsg_LocationChangeParams>& params);
   void OnAccessibilityFindInPageResult(
       const AccessibilityHostMsg_FindInPageResultParams& params);
+  void OnAccessibilitySnapshotResponse(int callback_id,
+                                       const ui::AXTreeUpdate& snapshot);
   void OnToggleFullscreen(bool enter_fullscreen);
 
 #if defined(OS_MACOSX) || defined(OS_ANDROID)
@@ -681,6 +690,10 @@
   // we don't keep trying to reset forever.
   int accessibility_reset_count_;
 
+  // The mapping from callback id to corresponding callback for pending
+  // accessibility tree snapshot calls created by RequestAXTreeSnapshot.
+  std::map<int, AXTreeSnapshotCallback> ax_tree_snapshot_callbacks_;
+
   // Callback when an event is received, for testing.
   base::Callback<void(ui::AXEvent, int)> accessibility_testing_callback_;
   // The most recently received accessibility tree - for testing only.
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h
index 91bf671..0be2701 100644
--- a/content/browser/frame_host/render_frame_host_manager.h
+++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -422,14 +422,11 @@
                                    SiteInstance* instance);
 
  private:
+  friend class FrameTreeVisualizer;
   friend class NavigatorTestWithBrowserSideNavigation;
   friend class RenderFrameHostManagerTest;
-  friend class SitePerProcessBrowserTest;
   friend class TestWebContents;
 
-  FRIEND_TEST_ALL_PREFIXES(CrossProcessFrameTreeBrowserTest,
-                           CreateCrossProcessSubframeProxies);
-
   // Stores information regarding a SiteInstance targeted at a specific URL to
   // allow for comparisons without having to actually create new instances. It
   // can point to an existing one or store the details needed to create a new
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 4de22b0..9de16f8 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -834,6 +834,50 @@
   EXPECT_EQ(orig_site_instance, revisit_site_instance);
 }
 
+// Test that subframes do not crash when sending a postMessage to the top frame
+// from an unload handler while the top frame is being swapped out as part of
+// navigating cross-process.  https://crbug.com/475651.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
+                       PostMessageFromSubframeUnloadHandler) {
+  StartServer();
+
+  GURL frame_url(test_server()->GetURL("files/post_message.html"));
+  GURL main_url("data:text/html,<iframe name='foo' src='" + frame_url.spec() +
+                "'></iframe>");
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Get the original SiteInstance for later comparison.
+  scoped_refptr<SiteInstance> orig_site_instance(
+      shell()->web_contents()->GetSiteInstance());
+  EXPECT_NE(nullptr, orig_site_instance.get());
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  ASSERT_EQ(1U, root->child_count());
+  EXPECT_EQ(frame_url, root->child_at(0)->current_url());
+
+  // Register an unload handler that sends a postMessage to the top frame.
+  EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(),
+                            "registerUnload();"));
+
+  // Navigate the top frame cross-site.  This will cause the top frame to be
+  // swapped out and run unload handlers, and the original renderer process
+  // should then terminate since it's not rendering any other frames.
+  RenderProcessHostWatcher exit_observer(
+      root->current_frame_host()->GetProcess(),
+      RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
+  EXPECT_TRUE(NavigateToURL(shell(), GetCrossSiteURL("files/title1.html")));
+  scoped_refptr<SiteInstance> new_site_instance(
+      shell()->web_contents()->GetSiteInstance());
+  EXPECT_NE(orig_site_instance, new_site_instance);
+
+  // Ensure that the original renderer process exited cleanly without crashing.
+  exit_observer.Wait();
+  EXPECT_EQ(true, exit_observer.did_exit_normally());
+}
+
 // Test that opening a new window in the same SiteInstance and then navigating
 // both windows to a different SiteInstance allows the first process to exit.
 // See http://crbug.com/126333.
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index 17d22b6..5d4b65d 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -167,7 +167,11 @@
 void RenderWidgetHostViewGuest::RenderProcessGone(
     base::TerminationStatus status,
     int error_code) {
-  platform_view_->RenderProcessGone(status, error_code);
+  // The |platform_view_| gets destroyed before we get here if this view
+  // is for an InterstitialPage.
+  if (platform_view_)
+    platform_view_->RenderProcessGone(status, error_code);
+
   // Destroy the guest view instance only, so we don't end up calling
   // platform_view_->Destroy().
   DestroyGuestView();
@@ -209,6 +213,12 @@
 }
 
 bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message& msg) {
+  if (!platform_view_) {
+    // In theory, we can get here if there's a delay between DestroyGuestView()
+    // being called and when our destructor is invoked.
+    return false;
+  }
+
   return platform_view_->OnMessageReceived(msg);
 }
 
diff --git a/content/browser/loader/buffered_resource_handler.cc b/content/browser/loader/buffered_resource_handler.cc
index 67e74a58..a38eaa915 100644
--- a/content/browser/loader/buffered_resource_handler.cc
+++ b/content/browser/loader/buffered_resource_handler.cc
@@ -110,16 +110,9 @@
                                                 bool* defer) {
   response_ = response;
 
-  // TODO(darin): It is very odd to special-case 304 responses at this level.
-  // We do so only because the code always has, see r24977 and r29355.  The
-  // fact that 204 is no longer special-cased this way suggests that 304 need
-  // not be special-cased either.
-  //
-  // The network stack only forwards 304 responses that were not received in
-  // response to a conditional request (i.e., If-Modified-Since).  Other 304
-  // responses end up being translated to 200 or whatever the cached response
-  // code happens to be.  It should be very rare to see a 304 at this level.
-
+  // A 304 response should not contain a Content-Type header (RFC 7232 section
+  // 4.1). The following code may incorrectly attempt to add a Content-Type to
+  // the response, and so must be skipped for 304 responses.
   if (!(response_->head.headers.get() &&
         response_->head.headers->response_code() == 304)) {
     if (ShouldSniffContent()) {
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc
index c4c6a141..24654377 100644
--- a/content/browser/net/sqlite_persistent_cookie_store.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -20,6 +20,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -560,6 +561,11 @@
 
 void SQLitePersistentCookieStore::Backend::CompleteLoadInForeground(
     const LoadedCallback& loaded_callback, bool load_success) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457528 "
+          "SQLitePersistentCookieStore::Backend::CompleteLoadInForeground"));
   Notify(loaded_callback, load_success);
 
   if (load_success)
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 47d0877..1ae9fe5 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -1623,15 +1623,30 @@
 }
 
 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
+  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 1"));
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (device_task_runner_.get())
     return;
 
   device_task_runner_ = audio_manager_->GetWorkerTaskRunner();
 
+  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 2"));
   audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
   audio_input_device_manager_->Register(this, device_task_runner_);
 
+  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile3(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 3"));
   // We want to be notified of IO message loop destruction to delete the thread
   // and the device managers.
   io_loop_ = base::MessageLoop::current();
@@ -1642,6 +1657,11 @@
     audio_input_device_manager()->UseFakeDevice();
   }
 
+  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile4(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 4"));
   video_capture_manager_ =
       new VideoCaptureManager(media::VideoCaptureDeviceFactory::CreateFactory(
           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)));
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 1cbd287..2e6299a 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -618,6 +618,8 @@
   // buffer.
   const size_t kScratchpadSizeInBytes = 400;
   unsigned char data[kScratchpadSizeInBytes];
+  // Initialize memory to satisfy DrMemory tests.
+  memset(data, 0, kScratchpadSizeInBytes);
   const gfx::Size capture_resolution(10, 10);
   ASSERT_GE(kScratchpadSizeInBytes, capture_resolution.GetArea() * 4u)
       << "Scratchpad is too small to hold the largest pixel format (ARGB).";
diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc
index 86dc692f..0fd5e45 100644
--- a/content/browser/renderer_host/overscroll_controller.cc
+++ b/content/browser/renderer_host/overscroll_controller.cc
@@ -35,11 +35,13 @@
 }
 
 bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
-  if (scroll_state_ != STATE_UNKNOWN) {
+  bool reset_scroll_state = false;
+  if (scroll_state_ != STATE_UNKNOWN ||
+      overscroll_delta_x_ || overscroll_delta_y_) {
     switch (event.type) {
       case blink::WebInputEvent::GestureScrollEnd:
       case blink::WebInputEvent::GestureFlingStart:
-        scroll_state_ = STATE_UNKNOWN;
+        reset_scroll_state = true;
         break;
 
       case blink::WebInputEvent::MouseWheel: {
@@ -48,7 +50,7 @@
         if (!wheel.hasPreciseScrollingDeltas ||
             wheel.phase == blink::WebMouseWheelEvent::PhaseEnded ||
             wheel.phase == blink::WebMouseWheelEvent::PhaseCancelled) {
-          scroll_state_ = STATE_UNKNOWN;
+          reset_scroll_state = true;
         }
         break;
       }
@@ -56,12 +58,15 @@
       default:
         if (blink::WebInputEvent::isMouseEventType(event.type) ||
             blink::WebInputEvent::isKeyboardEventType(event.type)) {
-          scroll_state_ = STATE_UNKNOWN;
+          reset_scroll_state = true;
         }
         break;
     }
   }
 
+  if (reset_scroll_state)
+    scroll_state_ = STATE_UNKNOWN;
+
   if (DispatchEventCompletesAction(event)) {
     CompleteAction();
 
@@ -80,8 +85,11 @@
     // Consume the event only if it updates the overscroll state.
     if (ProcessEventForOverscroll(event))
       return true;
+  } else if (reset_scroll_state) {
+    overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
   }
 
+
   return false;
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 8108477..2f48c18 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -16,6 +16,7 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/files/file.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -1578,6 +1579,11 @@
   LOG(ERROR) << "bad message " << message.type() << " terminating renderer.";
   BrowserChildProcessHostImpl::HistogramBadMessageTerminated(
       PROCESS_TYPE_RENDERER);
+
+  // Create a memory dump. This will contain enough stack frames to work out
+  // what the bad message was.
+  base::debug::DumpWithoutCrashing();
+
   bad_message::ReceivedBadMessage(this,
                                   bad_message::RPH_DESERIALIZATION_FAILED);
 }
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 3a7af0c..4966274 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -1218,7 +1218,7 @@
   delegate_->RequestToLockMouse(user_gesture, last_unlocked_by_target);
 }
 
-bool RenderViewHostImpl::IsFullscreen() const {
+bool RenderViewHostImpl::IsFullscreenGranted() const {
   return delegate_->IsFullscreenForCurrentTab();
 }
 
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 3e6b0154..470725a 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -327,7 +327,7 @@
   void OnRenderAutoResized(const gfx::Size& size) override;
   void RequestToLockMouse(bool user_gesture,
                           bool last_unlocked_by_target) override;
-  bool IsFullscreen() const override;
+  bool IsFullscreenGranted() const override;
   void OnFocus() override;
   void OnBlur() override;
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index fe0203ff..7dcae5a 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -594,7 +594,7 @@
     resize_params->top_controls_shrink_blink_size =
         view_->DoTopControlsShrinkBlinkSize();
     resize_params->visible_viewport_size = view_->GetVisibleViewportSize();
-    resize_params->is_fullscreen = IsFullscreen();
+    resize_params->is_fullscreen_granted = IsFullscreenGranted();
   }
 
   const bool size_changed =
@@ -606,7 +606,8 @@
       size_changed || screen_info_out_of_date_ ||
       old_resize_params_->physical_backing_size !=
           resize_params->physical_backing_size ||
-      old_resize_params_->is_fullscreen != resize_params->is_fullscreen ||
+      old_resize_params_->is_fullscreen_granted !=
+          resize_params->is_fullscreen_granted ||
       old_resize_params_->top_controls_height !=
           resize_params->top_controls_height ||
       old_resize_params_->top_controls_shrink_blink_size !=
@@ -1317,7 +1318,7 @@
   return view_ ? view_->IsMouseLocked() : false;
 }
 
-bool RenderWidgetHostImpl::IsFullscreen() const {
+bool RenderWidgetHostImpl::IsFullscreenGranted() const {
   return false;
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index bea0bfa..ecd86c52 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -539,8 +539,9 @@
 
   bool IsMouseLocked() const;
 
-  // RenderViewHost overrides this method to report when in fullscreen mode.
-  virtual bool IsFullscreen() const;
+  // RenderViewHost overrides this method to report whether tab-initiated
+  // fullscreen was granted.
+  virtual bool IsFullscreenGranted() const;
 
   // Indicates if the render widget host should track the render widget's size
   // as opposed to visa versa.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index f20df74..aba2b573 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -3324,4 +3324,49 @@
   EXPECT_EQ(2U, view_->dispatcher_->processed_touch_event_count());
 }
 
+// Tests that the scroll deltas stored within the overscroll controller get
+// reset at the end of the overscroll gesture even if the overscroll threshold
+// isn't surpassed and the overscroll mode stays OVERSCROLL_NONE.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, ScrollDeltasResetOnEnd) {
+  SetUpOverscrollEnvironment();
+  // Wheel event scroll ending with mouse move.
+  SimulateWheelEvent(-30, -10, 0, true);    // sent directly
+  SendInputEventACK(WebInputEvent::MouseWheel,
+                    INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+  EXPECT_EQ(-30.f, overscroll_delta_x());
+  EXPECT_EQ(-10.f, overscroll_delta_y());
+  SimulateMouseMove(5, 10, 0);
+  EXPECT_EQ(0.f, overscroll_delta_x());
+  EXPECT_EQ(0.f, overscroll_delta_y());
+
+  // Scroll gesture.
+  SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+                       blink::WebGestureDeviceTouchscreen);
+  SimulateGestureScrollUpdateEvent(-30, -5, 0);
+  SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+                    INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+  EXPECT_EQ(-30.f, overscroll_delta_x());
+  EXPECT_EQ(-5.f, overscroll_delta_y());
+  SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+                       blink::WebGestureDeviceTouchscreen);
+  EXPECT_EQ(0.f, overscroll_delta_x());
+  EXPECT_EQ(0.f, overscroll_delta_y());
+
+  // Wheel event scroll ending with a fling.
+  SimulateWheelEvent(5, 0, 0, true);
+  SendInputEventACK(WebInputEvent::MouseWheel,
+                    INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  SimulateWheelEvent(10, -5, 0, true);
+  SendInputEventACK(WebInputEvent::MouseWheel,
+                    INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+  EXPECT_EQ(15.f, overscroll_delta_x());
+  EXPECT_EQ(-5.f, overscroll_delta_y());
+  SimulateGestureFlingStartEvent(0.f, 0.1f, blink::WebGestureDeviceTouchpad);
+  EXPECT_EQ(0.f, overscroll_delta_x());
+  EXPECT_EQ(0.f, overscroll_delta_y());
+}
+
 }  // namespace content
diff --git a/content/browser/screen_orientation/screen_orientation_browsertest.cc b/content/browser/screen_orientation/screen_orientation_browsertest.cc
index 4b1d40b3..d7516cbf 100644
--- a/content/browser/screen_orientation/screen_orientation_browsertest.cc
+++ b/content/browser/screen_orientation/screen_orientation_browsertest.cc
@@ -60,7 +60,7 @@
     params.top_controls_height = 0.f;
     params.top_controls_shrink_blink_size = false;
     params.resizer_rect = gfx::Rect();
-    params.is_fullscreen = false;
+    params.is_fullscreen_granted = false;
     rwh->Send(new ViewMsg_Resize(rwh->GetRoutingID(), params));
   }
 
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index 5c10a67..69f0d858 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/service_worker/service_worker_metrics.h"
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -75,4 +75,24 @@
                           base::Bind(&RecordURLMetricOnUI, url));
 }
 
+void ServiceWorkerMetrics::RecordStartWorkerStatus(
+    ServiceWorkerStatusCode status,
+    bool is_installed) {
+  if (is_installed) {
+    UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status,
+                              SERVICE_WORKER_ERROR_MAX_VALUE);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartNewWorker.Status", status,
+                              SERVICE_WORKER_ERROR_MAX_VALUE);
+  }
+}
+
+void ServiceWorkerMetrics::RecordStartWorkerTime(const base::TimeDelta& time,
+                                                 bool is_installed) {
+  if (is_installed)
+    UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time", time);
+  else
+    UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartNewWorker.Time", time);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index 8b058567..705deb7 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -52,6 +52,16 @@
   // Counts the number of page loads controlled by a Service Worker.
   static void CountControlledPageLoad(const GURL& url);
 
+  // Records the result of trying to start a worker. |is_installed| indicates
+  // whether the version has been installed.
+  static void RecordStartWorkerStatus(ServiceWorkerStatusCode status,
+                                      bool is_installed);
+
+  // Records the time taken to successfully start a worker. |is_installed|
+  // indicates whether the version has been installed.
+  static void RecordStartWorkerTime(const base::TimeDelta& time,
+                                    bool is_installed);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceWorkerMetrics);
 };
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 0656833..e3667294 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -1451,9 +1451,11 @@
 void ServiceWorkerStorage::DidCollectStaleResources(
     const std::vector<int64>& stale_resource_ids,
     ServiceWorkerDatabase::Status status) {
-  DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
-  if (status != ServiceWorkerDatabase::STATUS_OK)
+  if (status != ServiceWorkerDatabase::STATUS_OK) {
+    DCHECK_NE(ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND, status);
+    ScheduleDeleteAndStartOver();
     return;
+  }
   StartPurgingResources(stale_resource_ids);
 }
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 681e2b6..0c5202d 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -19,6 +19,7 @@
 #include "content/browser/service_worker/embedded_worker_registry.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_utils.h"
 #include "content/browser/storage_partition_impl.h"
@@ -343,6 +344,21 @@
   clients->push_back(client_info);
 }
 
+bool IsInstalled(ServiceWorkerVersion::Status status) {
+  switch (status) {
+    case ServiceWorkerVersion::NEW:
+    case ServiceWorkerVersion::INSTALLING:
+    case ServiceWorkerVersion::REDUNDANT:
+      return false;
+    case ServiceWorkerVersion::INSTALLED:
+    case ServiceWorkerVersion::ACTIVATING:
+    case ServiceWorkerVersion::ACTIVATED:
+      return true;
+  }
+  NOTREACHED() << "Unexpected status: " << status;
+  return false;
+}
+
 }  // namespace
 
 const int ServiceWorkerVersion::kStartWorkerTimeoutMinutes = 5;
@@ -1731,15 +1747,15 @@
 
   // Failing to start a doomed worker isn't interesting and very common when
   // update dooms because the script is byte-to-byte identical.
-  if (is_doomed_)
+  if (is_doomed_ || status_ == REDUNDANT)
     return;
 
-  UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status,
-                            SERVICE_WORKER_ERROR_MAX_VALUE);
+  ServiceWorkerMetrics::RecordStartWorkerStatus(status, IsInstalled(status_));
+
   if (status == SERVICE_WORKER_OK && !start_time.is_null() &&
       !skip_recording_startup_time_) {
-    UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time",
-                               GetTickDuration(start_time));
+    ServiceWorkerMetrics::RecordStartWorkerTime(GetTickDuration(start_time),
+                                                IsInstalled(status_));
   }
 
   if (status != SERVICE_WORKER_ERROR_TIMEOUT)
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 15bd998..3e76965 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -169,21 +169,8 @@
 SitePerProcessBrowserTest::SitePerProcessBrowserTest() {
 };
 
-// static
-std::string SitePerProcessBrowserTest::DumpProxyHostSiteInstances(
-    FrameTreeNode* node) {
-  std::vector<std::string> sites;
-  for (auto& entry_pair : node->render_manager()->proxy_hosts_) {
-    sites.push_back(entry_pair.second->GetSiteInstance()->GetSiteURL().spec());
-  }
-  std::sort(sites.begin(), sites.end());
-  std::string result;
-  for (auto& site : sites) {
-    if (!result.empty())
-      result.append("\n");
-    result.append(site);
-  }
-  return result;
+std::string SitePerProcessBrowserTest::DepictFrameTree(FrameTreeNode* node) {
+  return visualizer_.DepictFrameTree(node);
 }
 
 void SitePerProcessBrowserTest::StartFrameAtDataURL() {
@@ -231,10 +218,16 @@
             ->GetRenderWidgetHostViewsInTree();
     EXPECT_EQ(1U, views_set.size());
   }
-  RenderFrameProxyHost* proxy_to_parent =
-      child->render_manager()->GetRenderFrameProxyHost(
-          shell()->web_contents()->GetSiteInstance());
-  EXPECT_FALSE(proxy_to_parent);
+
+  EXPECT_EQ(
+      " Site A\n"
+      "   |--Site A\n"
+      "   +--Site A\n"
+      "        |--Site A\n"
+      "        +--Site A\n"
+      "             +--Site A\n"
+      "Where A = http://127.0.0.1/",
+      DepictFrameTree(root));
 
   // Load cross-site page into iframe.
   GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
@@ -259,7 +252,8 @@
             ->GetRenderWidgetHostViewsInTree();
     EXPECT_EQ(2U, views_set.size());
   }
-  proxy_to_parent = child->render_manager()->GetProxyToParent();
+  RenderFrameProxyHost* proxy_to_parent =
+      child->render_manager()->GetProxyToParent();
   EXPECT_TRUE(proxy_to_parent);
   EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
   // The out-of-process iframe should have its own RenderWidgetHost,
@@ -269,6 +263,17 @@
       proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
   EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
 
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site B ------- proxies for A\n"
+      "   +--Site A ------- proxies for B\n"
+      "        |--Site A -- proxies for B\n"
+      "        +--Site A -- proxies for B\n"
+      "             +--Site A -- proxies for B\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://foo.com/",
+      DepictFrameTree(root));
+
   // Load another cross-site page into the same iframe.
   url = embedded_test_server()->GetURL("bar.com", "/title3.html");
   NavigateFrameToURL(root->child_at(0), url);
@@ -301,6 +306,17 @@
       child->current_frame_host()->render_view_host()->GetView(),
       proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
   EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for C\n"
+      "   |--Site C ------- proxies for A\n"
+      "   +--Site A ------- proxies for C\n"
+      "        |--Site A -- proxies for C\n"
+      "        +--Site A -- proxies for C\n"
+      "             +--Site A -- proxies for C\n"
+      "Where A = http://127.0.0.1/\n"
+      "      C = http://bar.com/",
+      DepictFrameTree(root));
 }
 
 // Tests OOPIF rendering by checking that the RWH of the iframe generates
@@ -418,6 +434,20 @@
       bar_child->current_frame_host()->GetSiteInstance();
   EXPECT_NE(shell()->web_contents()->GetSiteInstance(), bar_site_instance);
 
+// TODO(nick): The following EXPECTs are disabled because of
+// http://crbug.com/476628, where the Site C node sometimes (flakily) has
+// children even though it's committed a nav to a page with no iframes.
+#if 0
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   |--Site B ------- proxies for A C\n"
+      "   +--Site C ------- proxies for A B\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://foo.com/\n"
+      "      C = http://bar.com/",
+      DepictFrameTree(root));
+#endif
+
   // Simulate an attempt to detach the root frame from foo_site_instance.  This
   // should kill foo_site_instance's process.
   RenderFrameProxyHost* foo_mainframe_rfph =
@@ -429,6 +459,17 @@
   IPC::IpcSecurityTestUtil::PwnMessageReceived(
       foo_mainframe_rfph->GetProcess()->GetChannel(), evil_msg2);
   foo_terminated.Wait();
+
+#if 0
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   |--Site B ------- proxies for A C\n"
+      "   +--Site C ------- proxies for A B\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://foo.com/ (no process)\n"
+      "      C = http://bar.com/",
+      DepictFrameTree(root));
+#endif
 }
 
 // Disabled for flaky crashing: crbug.com/446575
@@ -628,9 +669,8 @@
 // site B and stays in not rendered state.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
                        NavigateRemoteFrameToKilledProcessWithSubtree) {
-  GURL main_url(
-      embedded_test_server()->GetURL(
-          "/frame_tree/page_with_two_frames_nested.html"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_two_frames_nested.html"));
   NavigateToURL(shell(), main_url);
 
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -666,24 +706,18 @@
   // below, so create a local scope so we can extend the lifetime of
   // |site_instance_c| with a refptr.
   {
-    SiteInstance* site_instance_b =
-        root->child_at(0)->current_frame_host()->GetSiteInstance();
-    // |site_c| will go away, so extend its lifetime with a refptr.
-    scoped_refptr<SiteInstanceImpl> site_instance_c =
-        node4->current_frame_host()->GetSiteInstance();
+    // Initially each frame has proxies for the other sites.
+    EXPECT_EQ(
+        " Site A ------------ proxies for B C\n"
+        "   |--Site B ------- proxies for A C\n"
+        "   |    +--Site C -- proxies for A B\n"
+        "   +--Site A ------- proxies for B C\n"
+        "Where A = http://a.com/\n"
+        "      B = http://bar.com/\n"
+        "      C = http://baz.com/",
+        DepictFrameTree(root));
 
-    // Initially proxies for both B and C will be present in the root and node3.
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_c.get()));
-    FrameTreeNode* node3 = root->child_at(1);
-    EXPECT_TRUE(node3->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    EXPECT_TRUE(node3->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_c.get()));
-
-    // Kill that cross-site renderer/process B.
+    // Kill the render process for Site B.
     RenderProcessHost* child_process_b =
         root->child_at(0)->current_frame_host()->GetProcess();
     RenderProcessHostWatcher crash_observer(
@@ -691,24 +725,31 @@
     child_process_b->Shutdown(0, false);
     crash_observer.Wait();
 
-    // Make sure proxy B stays around in root and node3.
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    EXPECT_TRUE(node3->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    // Make sure proxy C goes away from root and node3.
-    EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(
-                     site_instance_c.get()));
-    EXPECT_FALSE(node3->render_manager()->GetRenderFrameProxyHost(
-                     site_instance_c.get()));
+    // The Site C frame (a child of the crashed Site B frame) should go away,
+    // and there should be no remaining proxies for site C anywhere.
+    EXPECT_EQ(
+        " Site A ------------ proxies for B\n"
+        "   |--Site B ------- proxies for A\n"
+        "   +--Site A ------- proxies for B\n"
+        "Where A = http://a.com/\n"
+        "      B = http://bar.com/ (no process)",
+        DepictFrameTree(root));
   }
 
-  // Now navigate the second iframe (node3) to the same site as the node2.
+  // Now navigate the second iframe (node3) to Site B also.
   FrameTreeNode* node3 = root->child_at(1);
   GURL url = embedded_test_server()->GetURL("bar.com", "/title1.html");
   NavigateFrameToURL(node3, url);
   EXPECT_TRUE(observer.last_navigation_succeeded());
   EXPECT_EQ(url, observer.last_navigation_url());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site B ------- proxies for A\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://bar.com/",
+      DepictFrameTree(root));
 }
 
 // In A-embed-B-embed-C scenario, verify that killing process B clears proxies
@@ -725,9 +766,8 @@
 // After we kill B, make sure proxies for C are cleared.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
                        KillingRendererClearsDescendantProxies) {
-  GURL main_url(
-      embedded_test_server()->GetURL(
-          "/frame_tree/page_with_two_frames_nested.html"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_two_frames_nested.html"));
   NavigateToURL(shell(), main_url);
 
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -760,40 +800,40 @@
   EXPECT_EQ(site_c_url, node4->current_url());
 
   // |site_instance_c| is expected to go away once we kill |child_process_b|
-  // below, so create a local scope so we can extend the lifetime of
-  // |site_instance_c| with a refptr.
-  {
-    SiteInstance* site_instance_b =
-        root->child_at(0)->current_frame_host()->GetSiteInstance();
-    scoped_refptr<SiteInstanceImpl> site_instance_c =
-        node4->current_frame_host()->GetSiteInstance();
+  // below; refcount it to extend the lifetime.
+  scoped_refptr<SiteInstanceImpl> site_instance_c =
+      node4->current_frame_host()->GetSiteInstance();
 
-    // Initially proxies for both B and C will be present in the root.
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_c.get()));
+  // Initially proxies for both B and C will be present in the root.
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   |--Site B ------- proxies for A C\n"
+      "   |    +--Site C -- proxies for A B\n"
+      "   +--Site A ------- proxies for B C\n"
+      "Where A = http://a.com/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      DepictFrameTree(root));
+  // Kill process B.
+  RenderProcessHost* child_process_b =
+      root->child_at(0)->current_frame_host()->GetProcess();
+  RenderProcessHostWatcher crash_observer(
+      child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  child_process_b->Shutdown(0, false);
+  crash_observer.Wait();
 
-    // Kill process B.
-    RenderProcessHost* child_process_b =
-        root->child_at(0)->current_frame_host()->GetProcess();
-    RenderProcessHostWatcher crash_observer(
-        child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
-    child_process_b->Shutdown(0, false);
-    crash_observer.Wait();
+  // Make sure proxy C has gone from root.
+  // Make sure proxy C has gone from node3 as well.
+  // Make sure proxy B stays around in root and node3.
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site B ------- proxies for A\n"
+      "   +--Site A ------- proxies for B\n"
+      "Where A = http://a.com/\n"
+      "      B = http://bar.com/ (no process)",
+      DepictFrameTree(root));
 
-    // Make sure proxy C has gone from root.
-    EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(
-                     site_instance_c.get()));
-    // Make sure proxy C has gone from node3 as well.
-    EXPECT_FALSE(root->child_at(1)->render_manager()->GetRenderFrameProxyHost(
-                     site_instance_c.get()));
-    // Make sure proxy B stays around in root and node3.
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-    EXPECT_TRUE(root->child_at(1)->render_manager()->GetRenderFrameProxyHost(
-                    site_instance_b));
-  }
+  EXPECT_TRUE(site_instance_c->HasOneRef());
 }
 
 // Crash a subframe and ensures its children are cleared from the FrameTree.
@@ -866,8 +906,8 @@
 // checks that if A embeds B and later adds a new subframe A2, A2 gets a proxy
 // in B's process.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CreateProxiesForNewFrames) {
-  GURL main_url(
-      embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "b.com", "/frame_tree/page_with_one_frame.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -880,19 +920,28 @@
   EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
             root->child_at(0)->current_url());
 
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://b.com/\n"
+      "      B = http://baz.com/",
+      DepictFrameTree(root));
+
   // Add a new child frame to the top-level frame.
   RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
   EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
                             "window.domAutomationController.send("
                             "    addFrame('data:text/html,foo'));"));
   frame_observer.Wait();
-  ASSERT_EQ(2U, root->child_count());
 
-  // The new child frame should now have a proxy in first frame's process.
-  RenderFrameProxyHost* proxy =
-      root->child_at(1)->render_manager()->GetRenderFrameProxyHost(
-          root->child_at(0)->current_frame_host()->GetSiteInstance());
-  EXPECT_TRUE(proxy);
+  // The new frame should have a proxy in Site B, for use by the old frame.
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site B ------- proxies for A\n"
+      "   +--Site A ------- proxies for B\n"
+      "Where A = http://b.com/\n"
+      "      B = http://baz.com/",
+      DepictFrameTree(root));
 }
 
 // TODO(nasko): Disable this test until out-of-process iframes is ready and the
@@ -1138,10 +1187,15 @@
     NavigateFrameToURL(root->child_at(0), http_url);
     EXPECT_EQ(http_url, observer.last_navigation_url());
     EXPECT_TRUE(observer.last_navigation_succeeded());
-    RenderFrameProxyHost* proxy_to_parent =
-        root->child_at(0)->render_manager()->GetRenderFrameProxyHost(
-            shell()->web_contents()->GetSiteInstance());
-    EXPECT_FALSE(proxy_to_parent);
+    EXPECT_EQ(
+        " Site A\n"
+        "   |--Site A\n"
+        "   +--Site A\n"
+        "        |--Site A\n"
+        "        +--Site A\n"
+        "             +--Site A\n"
+        "Where A = http://127.0.0.1/",
+        DepictFrameTree(root));
   }
 
   // Create the cross-site URL to navigate to.
@@ -1162,19 +1216,21 @@
     params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
     params.frame_tree_node_id = child->frame_tree_node_id();
     child->navigator()->GetController()->LoadURLWithParams(params);
-    EXPECT_TRUE(child->render_manager()->pending_frame_host());
 
     site = child->render_manager()->pending_frame_host()->GetSiteInstance();
     EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site);
 
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site));
-    EXPECT_TRUE(
-        root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site));
-    EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site));
-    for (size_t i = 0; i < child->child_count(); ++i) {
-      EXPECT_FALSE(
-          child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site));
-    }
+    EXPECT_EQ(
+        " Site A ------------ proxies for B\n"
+        "   |--Site A ------- proxies for B\n"
+        "   +--Site A (B pending)\n"
+        "        |--Site A\n"
+        "        +--Site A\n"
+        "             +--Site A\n"
+        "Where A = http://127.0.0.1/\n"
+        "      B = http://foo.com/",
+        DepictFrameTree(root));
+
     // Now that the verification is done, run the message loop and wait for the
     // navigation to complete.
     navigation_observer.Wait();
@@ -1198,21 +1254,23 @@
     params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
     params.frame_tree_node_id = child->frame_tree_node_id();
     child->navigator()->GetController()->LoadURLWithParams(params);
-    EXPECT_TRUE(child->render_manager()->pending_frame_host() != NULL);
 
     SiteInstance* site2 =
         child->render_manager()->pending_frame_host()->GetSiteInstance();
     EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2);
     EXPECT_NE(site, site2);
 
-    EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site2));
-    EXPECT_TRUE(
-        root->child_at(0)->render_manager()->GetRenderFrameProxyHost(site2));
-    EXPECT_FALSE(child->render_manager()->GetRenderFrameProxyHost(site2));
-    for (size_t i = 0; i < child->child_count(); ++i) {
-      EXPECT_FALSE(
-          child->child_at(i)->render_manager()->GetRenderFrameProxyHost(site2));
-    }
+    EXPECT_EQ(
+        " Site A ------------ proxies for B C\n"
+        "   |--Site A ------- proxies for B C\n"
+        "   +--Site B (C pending) -- proxies for A\n"
+        "        |--Site A\n"
+        "        +--Site A\n"
+        "             +--Site A\n"
+        "Where A = http://127.0.0.1/\n"
+        "      B = http://foo.com/\n"
+        "      C = http://bar.com/",
+        DepictFrameTree(root));
 
     navigation_observer.Wait();
     EXPECT_TRUE(observer.last_navigation_succeeded());
@@ -1943,21 +2001,47 @@
   NavigateFrameToURL(root->child_at(0), url);
   EXPECT_TRUE(observer.last_navigation_succeeded());
   EXPECT_EQ(url, observer.last_navigation_url());
-  EXPECT_EQ("http://foo.com/", DumpProxyHostSiteInstances(root));
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   |--Site B ------- proxies for A\n"
+      "   +--Site A ------- proxies for B\n"
+      "        |--Site A -- proxies for B\n"
+      "        +--Site A -- proxies for B\n"
+      "             +--Site A -- proxies for B\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://foo.com/",
+      DepictFrameTree(root));
 
   // Load another cross-site page.
   url = embedded_test_server()->GetURL("bar.com", "/title3.html");
   NavigateIframeToURL(shell()->web_contents(), "test", url);
   EXPECT_TRUE(observer.last_navigation_succeeded());
   EXPECT_EQ(url, observer.last_navigation_url());
-  EXPECT_EQ("http://bar.com/", DumpProxyHostSiteInstances(root));
+  EXPECT_EQ(
+      " Site A ------------ proxies for C\n"
+      "   |--Site C ------- proxies for A\n"
+      "   +--Site A ------- proxies for C\n"
+      "        |--Site A -- proxies for C\n"
+      "        +--Site A -- proxies for C\n"
+      "             +--Site A -- proxies for C\n"
+      "Where A = http://127.0.0.1/\n"
+      "      C = http://bar.com/",
+      DepictFrameTree(root));
 
   // Navigate back to the parent's origin.
   url = embedded_test_server()->GetURL("/title1.html");
   NavigateFrameToURL(child, url);
   EXPECT_EQ(url, observer.last_navigation_url());
   EXPECT_TRUE(observer.last_navigation_succeeded());
-  EXPECT_EQ("", DumpProxyHostSiteInstances(root));
+  EXPECT_EQ(
+      " Site A\n"
+      "   |--Site A\n"
+      "   +--Site A\n"
+      "        |--Site A\n"
+      "        +--Site A\n"
+      "             +--Site A\n"
+      "Where A = http://127.0.0.1/",
+      DepictFrameTree(root));
 }
 
 }  // namespace content
diff --git a/content/browser/site_per_process_browsertest.h b/content/browser/site_per_process_browsertest.h
index 1e1ac4e..9e74bd7 100644
--- a/content/browser/site_per_process_browsertest.h
+++ b/content/browser/site_per_process_browsertest.h
@@ -2,9 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifndef CONTENT_BROWSER_SITE_PER_PROCESS_BROWSERTEST_H_
+#define CONTENT_BROWSER_SITE_PER_PROCESS_BROWSERTEST_H_
+
 #include <string>
 
 #include "content/public/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils_internal.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -16,19 +20,21 @@
  public:
   SitePerProcessBrowserTest();
 
-  // Returns an alphabetically-sorted, newline-delimited list of the site
-  // instance URLs in which RenderFrameProxyHosts of |node| currently exist.
-  // TODO(nick): Make this a full-fledged tree walk.
-  static std::string DumpProxyHostSiteInstances(FrameTreeNode* node);
-
  protected:
   // Start at a data URL so each extra navigation creates a navigation entry.
   // (The first navigation will silently be classified as AUTO_SUBFRAME.)
   // TODO(creis): This won't be necessary when we can wait for LOAD_STOP.
   void StartFrameAtDataURL();
 
+  std::string DepictFrameTree(FrameTreeNode* node);
+
   void SetUpCommandLine(base::CommandLine* command_line) override;
   void SetUpOnMainThread() override;
+
+ private:
+  FrameTreeVisualizer visualizer_;
 };
 
 }  // namespace content
+
+#endif  // CONTENT_BROWSER_SITE_PER_PROCESS_BROWSERTEST_H_
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 869946f..55cdbc7 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -168,6 +168,7 @@
       pending_trace_log_status_ack_count_(0),
       maximum_trace_buffer_usage_(0),
       approximate_event_count_(0),
+      failed_memory_dump_count_(0),
     // Tracing may have been enabled by ContentMainRunner if kTraceStartup
     // is specified in command line.
 #if defined(OS_CHROMEOS) || defined(OS_WIN)
@@ -649,7 +650,16 @@
                      base::trace_event::TraceLogStatus()));
     }
   }
-
+  TraceMessageFilterSet::const_iterator it =
+      pending_memory_dump_filters_.find(trace_message_filter);
+  if (it != pending_memory_dump_filters_.end()) {
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&TracingControllerImpl::OnProcessMemoryDumpResponse,
+                   base::Unretained(this),
+                   make_scoped_refptr(trace_message_filter),
+                   pending_memory_dump_guid_, false /* success */));
+  }
   trace_message_filters_.erase(trace_message_filter);
 }
 
@@ -890,11 +900,34 @@
                    base::Unretained(this), args, callback));
     return;
   }
-  // TODO(primiano): send a local dump request to each of the child processes
-  // and do the bookkeeping to keep track of the outstanding requests.
-  // Also, at this point, this should check for collisions and bail out if a
-  // global dump is requested while another is already in progress.
-  NOTIMPLEMENTED();
+  // Abort if another dump is already in progress.
+  if (pending_memory_dump_guid_) {
+    DVLOG(1) << "Requested memory dump " << args.dump_guid
+             << " while waiting for " << pending_memory_dump_guid_;
+    if (!callback.is_null())
+      callback.Run(args.dump_guid, false /* success */);
+    return;
+  }
+
+  pending_memory_dump_filters_.clear();
+  failed_memory_dump_count_ = 0;
+
+  base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(args);
+
+  // If there are no child processes we are just done.
+  TraceMessageFilterSet::iterator it = trace_message_filters_.begin();
+  if (it == trace_message_filters_.end()) {
+    if (!callback.is_null())
+      callback.Run(args.dump_guid, true /* success */);
+    return;
+  }
+
+  pending_memory_dump_guid_ = args.dump_guid;
+  pending_memory_dump_callback_ = callback;
+  pending_memory_dump_filters_ = trace_message_filters_;
+
+  for (; it != trace_message_filters_.end(); ++it)
+    it->get()->SendProcessMemoryDumpRequest(args);
 }
 
 void TracingControllerImpl::OnProcessMemoryDumpResponse(
@@ -910,11 +943,30 @@
                    success));
     return;
   }
-  // TODO(primiano): update the bookkeeping structs and, if this was the
-  // response from the last pending child, fire the completion callback, which
-  // in turn will cause a GlobalMemoryDumpResponse message to be sent back to
-  // the child, if this global dump was NOT initiated by the browser.
-  NOTIMPLEMENTED();
+
+  TraceMessageFilterSet::iterator it =
+      pending_memory_dump_filters_.find(trace_message_filter);
+
+  if (pending_memory_dump_guid_ != dump_guid ||
+      it == pending_memory_dump_filters_.end()) {
+    DLOG(WARNING) << "Received unexpected memory dump response: " << dump_guid;
+    return;
+  }
+
+  if (!success)
+    failed_memory_dump_count_++;
+
+  pending_memory_dump_filters_.erase(it);
+
+  if (pending_memory_dump_filters_.empty()) {
+    // Got response from all child proceses.
+    if (!pending_memory_dump_callback_.is_null()) {
+      const bool global_success = failed_memory_dump_count_ == 0;
+      pending_memory_dump_callback_.Run(dump_guid, global_success);
+      pending_memory_dump_callback_ = base::trace_event::MemoryDumpCallback();
+    }
+    pending_memory_dump_guid_ = 0;
+  }
 }
 
 void TracingControllerImpl::OnMonitoringStateChanged(bool is_monitoring) {
diff --git a/content/browser/tracing/tracing_controller_impl.h b/content/browser/tracing/tracing_controller_impl.h
index d22402cd..d62b514 100644
--- a/content/browser/tracing/tracing_controller_impl.h
+++ b/content/browser/tracing/tracing_controller_impl.h
@@ -156,15 +156,23 @@
   // Pending acks for DisableRecording.
   int pending_disable_recording_ack_count_;
   TraceMessageFilterSet pending_disable_recording_filters_;
+
   // Pending acks for CaptureMonitoringSnapshot.
   int pending_capture_monitoring_snapshot_ack_count_;
   TraceMessageFilterSet pending_capture_monitoring_filters_;
+
   // Pending acks for GetTraceLogStatus.
   int pending_trace_log_status_ack_count_;
   TraceMessageFilterSet pending_trace_log_status_filters_;
   float maximum_trace_buffer_usage_;
   size_t approximate_event_count_;
 
+  // Pending acks for memory RequestGlobalDumpPoint.
+  int failed_memory_dump_count_;
+  TraceMessageFilterSet pending_memory_dump_filters_;
+  uint64 pending_memory_dump_guid_;
+  base::trace_event::MemoryDumpCallback pending_memory_dump_callback_;
+
 #if defined(OS_CHROMEOS) || defined(OS_WIN)
   bool is_system_tracing_;
 #endif
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 5a41d12..4777b22 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -722,6 +722,13 @@
   SetAccessibilityMode(RemoveAccessibilityModeFrom(accessibility_mode_, mode));
 }
 
+void WebContentsImpl::RequestAXTreeSnapshot(AXTreeSnapshotCallback callback) {
+  // TODO(dmazzoni): http://crbug.com/475608 This only returns the
+  // accessibility tree from the main frame and everything in the
+  // same site instance.
+  GetMainFrame()->RequestAXTreeSnapshot(callback);
+}
+
 void WebContentsImpl::ClearNavigationTransitionData() {
   FrameTreeNode* node = frame_tree_.root();
   node->render_manager()->ClearNavigationTransitionData();
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index df36f49e..058d96d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -207,6 +207,12 @@
   // have been removed.
   void RemoveAccessibilityMode(AccessibilityMode mode);
 
+  // Request a one-time snapshot of the accessibility tree without changing
+  // the accessibility mode.
+  typedef base::Callback<void(const ui::AXTreeUpdate&)>
+      AXTreeSnapshotCallback;
+  void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback);
+
   // Clear the navigation transition data when the user navigates back to Chrome
   // from a native app.
   void ClearNavigationTransitionData();
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 32f102d..bd12aa5 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -1298,12 +1298,12 @@
   EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
 
   // Toggle fullscreen mode on (as if initiated via IPC from renderer).
-  EXPECT_FALSE(orig_rvh->IsFullscreen());
+  EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
   orig_rfh->OnMessageReceived(
       FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
-  EXPECT_TRUE(orig_rvh->IsFullscreen());
+  EXPECT_TRUE(orig_rvh->IsFullscreenGranted());
   EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
   EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
@@ -1317,7 +1317,7 @@
       pending_rfh, 1, url2, ui::PAGE_TRANSITION_TYPED);
 
   // Confirm fullscreen has exited.
-  EXPECT_FALSE(orig_rvh->IsFullscreen());
+  EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
@@ -1348,7 +1348,7 @@
   EXPECT_EQ(orig_rfh, contents()->GetMainFrame());
 
   // Sanity-check: Confirm we're not starting out in fullscreen mode.
-  EXPECT_FALSE(orig_rvh->IsFullscreen());
+  EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
@@ -1356,7 +1356,7 @@
     // Toggle fullscreen mode on (as if initiated via IPC from renderer).
     orig_rfh->OnMessageReceived(
         FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true));
-    EXPECT_TRUE(orig_rvh->IsFullscreen());
+    EXPECT_TRUE(orig_rvh->IsFullscreenGranted());
     EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
     EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
@@ -1371,7 +1371,7 @@
         orig_rfh, i + 1, url, ui::PAGE_TRANSITION_FORWARD_BACK);
 
     // Confirm fullscreen has exited.
-    EXPECT_FALSE(orig_rvh->IsFullscreen());
+    EXPECT_FALSE(orig_rvh->IsFullscreenGranted());
     EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
     EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
   }
@@ -1409,12 +1409,12 @@
       contents()->GetMainFrame(), 1, url, ui::PAGE_TRANSITION_TYPED);
 
   // Toggle fullscreen mode on (as if initiated via IPC from renderer).
-  EXPECT_FALSE(test_rvh()->IsFullscreen());
+  EXPECT_FALSE(test_rvh()->IsFullscreenGranted());
   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
   contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_ToggleFullscreen(
       contents()->GetMainFrame()->GetRoutingID(), true));
-  EXPECT_TRUE(test_rvh()->IsFullscreen());
+  EXPECT_TRUE(test_rvh()->IsFullscreenGranted());
   EXPECT_TRUE(contents()->IsFullscreenForCurrentTab());
   EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
@@ -1424,7 +1424,7 @@
           0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
 
   // Confirm fullscreen has exited.
-  EXPECT_FALSE(test_rvh()->IsFullscreen());
+  EXPECT_FALSE(test_rvh()->IsFullscreenGranted());
   EXPECT_FALSE(contents()->IsFullscreenForCurrentTab());
   EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents()));
 
diff --git a/content/child/scheduler/webthread_impl_for_worker_scheduler.cc b/content/child/scheduler/webthread_impl_for_worker_scheduler.cc
index bc4fe59..20379b121 100644
--- a/content/child/scheduler/webthread_impl_for_worker_scheduler.cc
+++ b/content/child/scheduler/webthread_impl_for_worker_scheduler.cc
@@ -26,7 +26,9 @@
 
 WebThreadImplForWorkerScheduler::~WebThreadImplForWorkerScheduler() {
   base::WaitableEvent completion(false, false);
-  thread_->message_loop()->PostTask(
+  // We need to post the shutdown task on the scheduler's task queue or tasks
+  // posted on the worker scheduler may not get run when the thread is deleted.
+  TaskRunner()->PostTask(
       FROM_HERE, base::Bind(&WebThreadImplForWorkerScheduler::ShutDownOnThread,
                             base::Unretained(this), &completion));
   completion.Wait();
diff --git a/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc b/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc
index d8da51f..b03749a 100644
--- a/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc
+++ b/content/child/scheduler/webthread_impl_for_worker_scheduler_unittest.cc
@@ -79,14 +79,18 @@
 
 class WebThreadImplForWorkerSchedulerTest : public testing::Test {
  public:
-  WebThreadImplForWorkerSchedulerTest() : thread_("test thread") {}
+  WebThreadImplForWorkerSchedulerTest() {}
 
   ~WebThreadImplForWorkerSchedulerTest() override {}
 
+  void SetUp() override {
+    thread_.reset(new WebThreadImplForWorkerScheduler("test thread"));
+  }
+
   void RunOnWorkerThread(const tracked_objects::Location& from_here,
                          const base::Closure& task) {
     base::WaitableEvent completion(false, false);
-    thread_.TaskRunner()->PostTask(
+    thread_->TaskRunner()->PostTask(
         from_here,
         base::Bind(&WebThreadImplForWorkerSchedulerTest::RunOnWorkerThreadTask,
                    base::Unretained(this), task, &completion));
@@ -100,7 +104,7 @@
     completion->Signal();
   }
 
-  WebThreadImplForWorkerScheduler thread_;
+  scoped_ptr<WebThreadImplForWorkerScheduler> thread_;
 
   DISALLOW_COPY_AND_ASSIGN(WebThreadImplForWorkerSchedulerTest);
 };
@@ -113,10 +117,23 @@
   ON_CALL(*task, run())
       .WillByDefault(Invoke([&completion]() { completion.Signal(); }));
 
-  thread_.postTask(blink::WebTraceLocation(), task.release());
+  thread_->postTask(blink::WebTraceLocation(), task.release());
   completion.Wait();
 }
 
+TEST_F(WebThreadImplForWorkerSchedulerTest,
+       TestTaskExecutedBeforeThreadDeletion) {
+  scoped_ptr<MockTask> task(new MockTask());
+  base::WaitableEvent completion(false, false);
+
+  EXPECT_CALL(*task, run());
+  ON_CALL(*task, run())
+      .WillByDefault(Invoke([&completion]() { completion.Signal(); }));
+
+  thread_->postTask(blink::WebTraceLocation(), task.release());
+  thread_.reset();
+}
+
 TEST_F(WebThreadImplForWorkerSchedulerTest, TestIdleTask) {
   scoped_ptr<MockIdleTask> task(new MockIdleTask());
   base::WaitableEvent completion(false, false);
@@ -125,9 +142,9 @@
   ON_CALL(*task, run(_))
       .WillByDefault(Invoke([&completion](double) { completion.Signal(); }));
 
-  thread_.postIdleTask(blink::WebTraceLocation(), task.release());
+  thread_->postIdleTask(blink::WebTraceLocation(), task.release());
   // We need to post a wakeup task or idle work will never happen.
-  thread_.postDelayedTask(blink::WebTraceLocation(), new NopTask(), 50ul);
+  thread_->postDelayedTask(blink::WebTraceLocation(), new NopTask(), 50ul);
 
   completion.Wait();
 }
@@ -137,10 +154,10 @@
   TestObserver observer(&calls);
 
   RunOnWorkerThread(FROM_HERE,
-                    base::Bind(&addTaskObserver, &thread_, &observer));
-  thread_.postTask(blink::WebTraceLocation(), new TestTask(&calls));
+                    base::Bind(&addTaskObserver, thread_.get(), &observer));
+  thread_->postTask(blink::WebTraceLocation(), new TestTask(&calls));
   RunOnWorkerThread(FROM_HERE,
-                    base::Bind(&removeTaskObserver, &thread_, &observer));
+                    base::Bind(&removeTaskObserver, thread_.get(), &observer));
 
   // We need to be careful what we test here.  We want to make sure the
   // observers are un in the expected order before and after the task.
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h
index 69dfb51..e7d0bcdf 100644
--- a/content/common/accessibility_messages.h
+++ b/content/common/accessibility_messages.h
@@ -178,6 +178,12 @@
 // and we've already reset too many times.
 IPC_MESSAGE_ROUTED0(AccessibilityMsg_FatalError)
 
+// Request a one-time snapshot of the accessibility tree without
+// enabling accessibility if it wasn't already enabled. The passed id
+// will be returned in the AccessibilityHostMsg_SnapshotResponse message.
+IPC_MESSAGE_ROUTED1(AccessibilityMsg_SnapshotTree,
+                    int /* callback id */)
+
 // Messages sent from the renderer to the browser.
 
 // Sent to notify the browser about renderer accessibility events.
@@ -200,3 +206,10 @@
 IPC_MESSAGE_ROUTED1(
     AccessibilityHostMsg_FindInPageResult,
     AccessibilityHostMsg_FindInPageResultParams)
+
+// Sent in response to AccessibilityMsg_SnapshotTree. The callback id that was
+// passed to the request will be returned in |callback_id|, along with
+// a standalone snapshot of the accessibility tree.
+IPC_MESSAGE_ROUTED2(AccessibilityHostMsg_SnapshotResponse,
+                    int /* callback_id */,
+                    ui::AXTreeUpdate)
diff --git a/content/common/sandbox_linux/sandbox_linux.cc b/content/common/sandbox_linux/sandbox_linux.cc
index 5eee4e1..b2a7b3e 100644
--- a/content/common/sandbox_linux/sandbox_linux.cc
+++ b/content/common/sandbox_linux/sandbox_linux.cc
@@ -186,7 +186,12 @@
   // Note: this requires SealSandbox() to be called later in this process to be
   // safe, as this class is keeping a file descriptor to /proc/.
   CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_));
-  CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_));
+
+  // We do not drop CAP_SYS_ADMIN because we need it to place each child process
+  // in its own PID namespace later on.
+  std::vector<sandbox::Credentials::Capability> caps;
+  caps.push_back(sandbox::Credentials::Capability::SYS_ADMIN);
+  CHECK(sandbox::Credentials::SetCapabilities(proc_fd_, caps));
 
   // This needs to happen after moving to a new user NS, since doing so involves
   // writing the UID/GID map.
diff --git a/content/common/sandbox_linux/sandbox_linux.h b/content/common/sandbox_linux/sandbox_linux.h
index 12aabcfc..01a0bd9a 100644
--- a/content/common/sandbox_linux/sandbox_linux.h
+++ b/content/common/sandbox_linux/sandbox_linux.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "content/public/common/sandbox_linux.h"
 
@@ -118,6 +119,13 @@
   // to make some vulnerabilities harder to exploit.
   bool LimitAddressSpace(const std::string& process_type);
 
+  // Returns a file descriptor to proc. The file descriptor is no longer valid
+  // after the sandbox has been sealed.
+  int proc_fd() const {
+    DCHECK_NE(-1, proc_fd_);
+    return proc_fd_;
+  }
+
 #if defined(ANY_OF_AMTLU_SANITIZER)
   __sanitizer_sandbox_arguments* sanitizer_args() const {
     return sanitizer_args_.get();
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index b8acc3ac..85134356 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -470,8 +470,8 @@
   IPC_STRUCT_MEMBER(gfx::Size, visible_viewport_size)
   // The resizer rect.
   IPC_STRUCT_MEMBER(gfx::Rect, resizer_rect)
-  // Indicates whether a page is fullscreen or not.
-  IPC_STRUCT_MEMBER(bool, is_fullscreen)
+  // Indicates whether tab-initiated fullscreen was granted.
+  IPC_STRUCT_MEMBER(bool, is_fullscreen_granted)
   // If set, requests the renderer to reply with a ViewHostMsg_UpdateRect
   // with the ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK bit set in flags.
   IPC_STRUCT_MEMBER(bool, needs_resize_ack)
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index c1726787..117d20a 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -521,6 +521,8 @@
       'browser/device_sensors/sensor_manager_android.h',
       'browser/device_sensors/sensor_manager_chromeos.cc',
       'browser/device_sensors/sensor_manager_chromeos.h',
+      'browser/devtools/browser_devtools_agent_host.cc',
+      'browser/devtools/browser_devtools_agent_host.h',
       'browser/devtools/devtools_agent_host_impl.cc',
       'browser/devtools/devtools_agent_host_impl.h',
       'browser/devtools/devtools_frame_trace_recorder.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 4dd854f..93791e19 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -186,6 +186,7 @@
       'browser/accessibility/dump_accessibility_browsertest_base.h',
       'browser/accessibility/dump_accessibility_events_browsertest.cc',
       'browser/accessibility/dump_accessibility_tree_browsertest.cc',
+      'browser/accessibility/snapshot_ax_tree_browsertest.cc',
       'browser/accessibility/site_per_process_accessibility_browsertest.cc',
       'browser/battery_status/battery_monitor_impl_browsertest.cc',
       'browser/battery_status/battery_monitor_integration_browsertest.cc',
diff --git a/content/public/android/java/src/org/chromium/content/browser/OWNERS b/content/public/android/java/src/org/chromium/content/browser/OWNERS
index 9e085b0..66d3fd4 100644
--- a/content/public/android/java/src/org/chromium/content/browser/OWNERS
+++ b/content/public/android/java/src/org/chromium/content/browser/OWNERS
@@ -9,9 +9,6 @@
 # Screen Orientation API related
 per-file ScreenOrientation*.java=mlamouri@chromium.org
 
-# Battery Status API related
-per-file BatteryStatusManager.java=timvolodine@chromium.org
-
 # Input handling related
 jdduke@chromium.org
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/SmartClipProvider.java b/content/public/android/java/src/org/chromium/content/browser/SmartClipProvider.java
index e1502b6..ae38c04 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SmartClipProvider.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SmartClipProvider.java
@@ -6,7 +6,7 @@
 
 import android.os.Handler;
 
-import org.chromium.base.UsedByReflection;
+import org.chromium.base.annotations.UsedByReflection;
 
 /**
  * An interface to provide smart clip data when requested.
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/OWNERS b/content/public/android/javatests/src/org/chromium/content/browser/OWNERS
index 36b349e..966545b 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/OWNERS
+++ b/content/public/android/javatests/src/org/chromium/content/browser/OWNERS
@@ -9,9 +9,6 @@
 # Screen Orientation API related
 per-file ScreenOrientation*.java=mlamouri@chromium.org
 
-# Battery Status API related
-per-file BatteryStatusManagerTest.java=timvolodine@chromium.org
-
 # Input handling related
 jdduke@chromium.org
 
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index b3b42290..9473c2cd 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -184,6 +184,16 @@
       const tracked_objects::Location& from_here,
       const base::Closure& task);
 
+  // For use with scheduling non-critical tasks for execution after startup.
+  // The order or execution of tasks posted here is unspecified even when
+  // posting to a SequencedTaskRunner and tasks are not guaranteed to be run
+  // prior to browser shutdown.
+  // Note: see related ContentBrowserClient::PostAfterStartupTask.
+  static void PostAfterStartupTask(
+      const tracked_objects::Location& from_here,
+      const scoped_refptr<base::TaskRunner>& task_runner,
+      const base::Closure& task);
+
   // Returns the thread pool used for blocking file I/O. Use this object to
   // perform random blocking operations such as file writes or querying the
   // Windows registry.
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index e50b963..41feccb 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -16,6 +16,13 @@
   return nullptr;
 }
 
+void ContentBrowserClient::PostAfterStartupTask(
+    const tracked_objects::Location& from_here,
+    const scoped_refptr<base::TaskRunner>& task_runner,
+    const base::Closure& task) {
+  task_runner->PostTask(from_here, task);
+}
+
 WebContentsViewDelegate* ContentBrowserClient::GetWebContentsViewDelegate(
     WebContents* web_contents) {
   return nullptr;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index a0ac3c2..842169cd 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -140,6 +140,16 @@
   virtual BrowserMainParts* CreateBrowserMainParts(
       const MainFunctionParams& parameters);
 
+  // Allows the embedder to change the default behavior of
+  // BrowserThread::PostAfterStartupTask to better match whatever
+  // definition of "startup" the embedder has in mind. This may be
+  // called on any thread.
+  // Note: see related BrowserThread::PostAfterStartupTask.
+  virtual void PostAfterStartupTask(
+      const tracked_objects::Location& from_here,
+      const scoped_refptr<base::TaskRunner>& task_runner,
+      const base::Closure& task);
+
   // If content creates the WebContentsView implementation, it will ask the
   // embedder to return an (optional) delegate to customize it. The view will
   // own the delegate.
diff --git a/content/public/browser/devtools_agent_host.h b/content/public/browser/devtools_agent_host.h
index 49feaf1..12616c1 100644
--- a/content/public/browser/devtools_agent_host.h
+++ b/content/public/browser/devtools_agent_host.h
@@ -11,10 +11,16 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/devtools_agent_host_client.h"
 #include "url/gurl.h"
 
+namespace net {
+class ServerSocket;
+}
+
 namespace content {
 
 class BrowserContext;
@@ -40,8 +46,17 @@
 
     // Agent host associated with DevToolsExternalAgentProxyDelegate.
     TYPE_EXTERNAL,
+
+    // Agent host associated with browser.
+    TYPE_BROWSER,
   };
 
+  // Latest DevTools protocol version supported.
+  static std::string GetProtocolVersion();
+
+  // Returns whether particular version of DevTools protocol is supported.
+  static bool IsSupportedProtocolVersion(const std::string& version);
+
   // Returns DevToolsAgentHost with a given |id| or nullptr of it doesn't exist.
   static scoped_refptr<DevToolsAgentHost> GetForId(const std::string& id);
 
@@ -65,6 +80,15 @@
   static scoped_refptr<DevToolsAgentHost> Create(
       DevToolsExternalAgentProxyDelegate* delegate);
 
+  using CreateServerSocketCallback =
+      base::Callback<scoped_ptr<net::ServerSocket>(std::string*)>;
+
+  // Creates DevToolsAgentHost for the browser, which works with browser-wide
+  // debugging protocol.
+  static scoped_refptr<DevToolsAgentHost> CreateForBrowser(
+      scoped_refptr<base::MessageLoopProxy> tethering_message_loop,
+      const CreateServerSocketCallback& socket_callback);
+
   static bool IsDebuggerAttached(WebContents* web_contents);
 
   typedef std::vector<scoped_refptr<DevToolsAgentHost> > List;
@@ -103,9 +127,6 @@
   // Attaches render view host to this host.
   virtual void ConnectWebContents(WebContents* web_contents) = 0;
 
-  // Returns true if DevToolsAgentHost is for worker.
-  virtual bool IsWorker() const = 0;
-
   // Returns agent host type.
   virtual Type GetType() = 0;
 
diff --git a/content/public/browser/devtools_http_handler.h b/content/public/browser/devtools_http_handler.h
index 1c79ef08..a61901b 100644
--- a/content/public/browser/devtools_http_handler.h
+++ b/content/public/browser/devtools_http_handler.h
@@ -15,7 +15,6 @@
 
 namespace net {
 class ServerSocket;
-class URLRequestContextGetter;
 }
 
 namespace content {
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index afb07ba..99c1da4d 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -841,6 +841,7 @@
     RenderProcessHost* render_process_host, WatchType type)
     : render_process_host_(render_process_host),
       type_(type),
+      did_exit_normally_(true),
       message_loop_runner_(new MessageLoopRunner) {
   render_process_host_->AddObserver(this);
 }
@@ -849,6 +850,7 @@
     WebContents* web_contents, WatchType type)
     : render_process_host_(web_contents->GetRenderProcessHost()),
       type_(type),
+      did_exit_normally_(true),
       message_loop_runner_(new MessageLoopRunner) {
   render_process_host_->AddObserver(this);
 }
@@ -866,6 +868,7 @@
     RenderProcessHost* host,
     base::TerminationStatus status,
     int exit_code) {
+  did_exit_normally_ = status == base::TERMINATION_STATUS_NORMAL_TERMINATION;
   if (type_ == WATCH_FOR_PROCESS_EXIT)
     message_loop_runner_->Quit();
 }
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 7c2a20b..bcc424f 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -324,6 +324,11 @@
   // Waits until the renderer process exits.
   void Wait();
 
+  // Returns true if a renderer process exited cleanly (without hitting
+  // RenderProcessExited with an abnormal TerminationStatus). This should be
+  // called after Wait().
+  bool did_exit_normally() { return did_exit_normally_; }
+
  private:
   // Overridden RenderProcessHost::LifecycleObserver methods.
   void RenderProcessExited(RenderProcessHost* host,
@@ -333,6 +338,7 @@
 
   RenderProcessHost* render_process_host_;
   WatchType type_;
+  bool did_exit_normally_;
 
   scoped_refptr<MessageLoopRunner> message_loop_runner_;
 
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 935b219..1513b8d8 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -380,7 +380,7 @@
 
 void RenderViewTest::Resize(gfx::Size new_size,
                             gfx::Rect resizer_rect,
-                            bool is_fullscreen) {
+                            bool is_fullscreen_granted) {
   ViewMsg_Resize_Params params;
   params.screen_info = blink::WebScreenInfo();
   params.new_size = new_size;
@@ -388,7 +388,7 @@
   params.top_controls_height = 0.f;
   params.top_controls_shrink_blink_size = false;
   params.resizer_rect = resizer_rect;
-  params.is_fullscreen = is_fullscreen;
+  params.is_fullscreen_granted = is_fullscreen_granted;
   scoped_ptr<IPC::Message> resize_message(new ViewMsg_Resize(0, params));
   OnMessageReceived(*resize_message);
 }
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc
index 0707716..1ad905da 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.cc
+++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -117,6 +117,10 @@
 BlinkAXTreeSource::~BlinkAXTreeSource() {
 }
 
+void BlinkAXTreeSource::SetRoot(blink::WebAXObject root) {
+  root_ = root;
+}
+
 bool BlinkAXTreeSource::IsInTree(blink::WebAXObject node) const {
   const blink::WebAXObject& root = GetRoot();
   while (IsValid(node)) {
@@ -136,6 +140,8 @@
 }
 
 blink::WebAXObject BlinkAXTreeSource::GetRoot() const {
+  if (!root_.isNull())
+    return root_;
   return GetMainDocument().accessibilityObject();
 }
 
diff --git a/content/renderer/accessibility/blink_ax_tree_source.h b/content/renderer/accessibility/blink_ax_tree_source.h
index b08108b..ae93ff4 100644
--- a/content/renderer/accessibility/blink_ax_tree_source.h
+++ b/content/renderer/accessibility/blink_ax_tree_source.h
@@ -19,6 +19,11 @@
   BlinkAXTreeSource(RenderFrameImpl* render_frame);
   ~BlinkAXTreeSource() override;
 
+  // It may be necessary to call SetRoot if you're using a WebScopedAXContext,
+  // because BlinkAXTreeSource can't get the root of the tree from the
+  // WebDocument if accessibility isn't enabled globally.
+  void SetRoot(blink::WebAXObject root);
+
   // Call this to have BlinkAXTreeSource collect a mapping from
   // node ids to the frame routing id for an out-of-process iframe during
   // calls to SerializeNode.
@@ -54,6 +59,7 @@
 
  private:
   RenderFrameImpl* render_frame_;
+  blink::WebAXObject root_;
   std::map<int32, int>* node_to_frame_routing_id_map_;
   std::map<int32, int>* node_to_browser_plugin_instance_id_map_;
   int accessibility_focus_id_;
diff --git a/content/renderer/accessibility/renderer_accessibility.cc b/content/renderer/accessibility/renderer_accessibility.cc
index b8152ba..12a59d3 100644
--- a/content/renderer/accessibility/renderer_accessibility.cc
+++ b/content/renderer/accessibility/renderer_accessibility.cc
@@ -25,11 +25,29 @@
 using blink::WebNode;
 using blink::WebPoint;
 using blink::WebRect;
+using blink::WebScopedAXContext;
 using blink::WebSettings;
 using blink::WebView;
 
 namespace content {
 
+// static
+void RendererAccessibility::SnapshotAccessibilityTree(
+    RenderFrameImpl* render_frame,
+    ui::AXTreeUpdate* response) {
+  DCHECK(render_frame);
+  DCHECK(response);
+  if (!render_frame->GetWebFrame())
+    return;
+
+  WebDocument document = render_frame->GetWebFrame()->document();
+  WebScopedAXContext context(document);
+  BlinkAXTreeSource tree_source(render_frame);
+  tree_source.SetRoot(context.root());
+  ui::AXTreeSerializer<blink::WebAXObject> serializer(&tree_source);
+  serializer.SerializeChanges(context.root(), response);
+}
+
 RendererAccessibility::RendererAccessibility(RenderFrameImpl* render_frame)
     : RenderFrameObserver(render_frame),
       render_frame_(render_frame),
diff --git a/content/renderer/accessibility/renderer_accessibility.h b/content/renderer/accessibility/renderer_accessibility.h
index be0ca59..5ab857f 100644
--- a/content/renderer/accessibility/renderer_accessibility.h
+++ b/content/renderer/accessibility/renderer_accessibility.h
@@ -46,6 +46,11 @@
 // (e.g., change focus, or click on a button).
 class CONTENT_EXPORT RendererAccessibility : public RenderFrameObserver {
  public:
+  // Request a one-time snapshot of the accessibility tree without
+  // enabling accessibility if it wasn't already enabled.
+  static void SnapshotAccessibilityTree(RenderFrameImpl* render_frame,
+                                        ui::AXTreeUpdate* response);
+
   explicit RendererAccessibility(RenderFrameImpl* render_frame);
   ~RendererAccessibility() override;
 
diff --git a/content/renderer/media/DEPS b/content/renderer/media/DEPS
new file mode 100644
index 0000000..a6cd8ed
--- /dev/null
+++ b/content/renderer/media/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  # For video copying, cropping and scaling.
+  # TODO(wuchengli): remove this when RTCVideoEncoder supports zero copy.
+  "+third_party/libyuv",
+]
+
diff --git a/content/renderer/media/android/webmediaplayer_android.cc b/content/renderer/media/android/webmediaplayer_android.cc
index a8188bd..77790046 100644
--- a/content/renderer/media/android/webmediaplayer_android.cc
+++ b/content/renderer/media/android/webmediaplayer_android.cc
@@ -1527,48 +1527,35 @@
   if (!IsKeySystemSupported(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
-  // We do not support run-time switching between key systems for now.
-  if (current_key_system_.empty()) {
-    if (!proxy_decryptor_) {
-      proxy_decryptor_.reset(new media::ProxyDecryptor(
-          media_permission_,
-          base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
-                     weak_factory_.GetWeakPtr()),
-          base::Bind(&WebMediaPlayerAndroid::OnKeyError,
-                     weak_factory_.GetWeakPtr()),
-          base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
-                     weak_factory_.GetWeakPtr())));
-    }
+  if (!proxy_decryptor_) {
+    DCHECK(current_key_system_.empty());
+    proxy_decryptor_.reset(new media::ProxyDecryptor(
+        media_permission_, base::Bind(&WebMediaPlayerAndroid::OnKeyAdded,
+                                      weak_factory_.GetWeakPtr()),
+        base::Bind(&WebMediaPlayerAndroid::OnKeyError,
+                   weak_factory_.GetWeakPtr()),
+        base::Bind(&WebMediaPlayerAndroid::OnKeyMessage,
+                   weak_factory_.GetWeakPtr())));
 
     GURL security_origin(frame_->document().securityOrigin().toString());
-    if (!proxy_decryptor_->InitializeCDM(cdm_factory_, key_system,
-                                         security_origin)) {
-      return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
-    }
-
-    // Set the CDM onto the media player.
-    cdm_context_ = proxy_decryptor_->GetCdmContext();
-
-    if (is_player_initialized_)
-      SetCdmInternal(base::Bind(&media::IgnoreCdmAttached));
-
+    proxy_decryptor_->CreateCdm(
+        cdm_factory_, key_system, security_origin,
+        base::Bind(&WebMediaPlayerAndroid::OnCdmContextReady,
+                   weak_factory_.GetWeakPtr()));
     current_key_system_ = key_system;
-  } else if (key_system != current_key_system_) {
-    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
   }
 
+  // We do not support run-time switching between key systems for now.
+  DCHECK(!current_key_system_.empty());
+  if (key_system != current_key_system_)
+    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
+
   media::EmeInitDataType init_data_type = init_data_type_;
   if (init_data_type == media::EmeInitDataType::UNKNOWN)
     init_data_type = GuessInitDataType(init_data, init_data_length);
 
-  // TODO(xhwang): We assume all streams are from the same container (thus have
-  // the same "type") for now. In the future, the "type" should be passed down
-  // from the application.
-  if (!proxy_decryptor_->GenerateKeyRequest(
-           init_data_type, init_data, init_data_length)) {
-    current_key_system_.clear();
-    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
-  }
+  proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
+                                       init_data_length);
 
   return WebMediaPlayer::MediaKeyExceptionNoError;
 }
@@ -1772,6 +1759,20 @@
   client_->didResumePlaybackBlockedForKey();
 }
 
+void WebMediaPlayerAndroid::OnCdmContextReady(media::CdmContext* cdm_context) {
+  DCHECK(!cdm_context_);
+
+  if (!cdm_context) {
+    LOG(ERROR) << "CdmContext not available (e.g. CDM creation failed).";
+    return;
+  }
+
+  cdm_context_ = cdm_context;
+
+  if (is_player_initialized_)
+    SetCdmInternal(base::Bind(&media::IgnoreCdmAttached));
+}
+
 void WebMediaPlayerAndroid::SetCdmInternal(
     const media::CdmAttachedCB& cdm_attached_cb) {
   DCHECK(cdm_context_ && is_player_initialized_);
diff --git a/content/renderer/media/android/webmediaplayer_android.h b/content/renderer/media/android/webmediaplayer_android.h
index 2294b7c..c1e30101 100644
--- a/content/renderer/media/android/webmediaplayer_android.h
+++ b/content/renderer/media/android/webmediaplayer_android.h
@@ -318,6 +318,9 @@
   MediaKeyException CancelKeyRequestInternal(const std::string& key_system,
                                              const std::string& session_id);
 
+  // Called when |cdm_context| is ready.
+  void OnCdmContextReady(media::CdmContext* cdm_context);
+
   // Sets the CDM. Should only be called when |is_player_initialized_| is true
   // and a new non-null |cdm_context_| is available. Fires |cdm_attached_cb_|
   // with the result after the CDM is attached.
diff --git a/content/renderer/media/crypto/render_cdm_factory.cc b/content/renderer/media/crypto/render_cdm_factory.cc
index c871f9c..e551226f 100644
--- a/content/renderer/media/crypto/render_cdm_factory.cc
+++ b/content/renderer/media/crypto/render_cdm_factory.cc
@@ -4,11 +4,13 @@
 
 #include "content/renderer/media/crypto/render_cdm_factory.h"
 
+#include "base/bind.h"
+#include "base/location.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "media/base/key_systems.h"
 #include "media/cdm/aes_decryptor.h"
 #include "url/gurl.h"
-
 #if defined(ENABLE_PEPPER_CDMS)
 #include "content/renderer/media/crypto/ppapi_decryptor.h"
 #elif defined(ENABLE_BROWSER_CDMS)
@@ -37,7 +39,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
-scoped_ptr<media::MediaKeys> RenderCdmFactory::Create(
+void RenderCdmFactory::Create(
     const std::string& key_system,
     bool allow_distinctive_identifier,
     bool allow_persistent_state,
@@ -46,38 +48,45 @@
     const media::SessionClosedCB& session_closed_cb,
     const media::LegacySessionErrorCB& legacy_session_error_cb,
     const media::SessionKeysChangeCB& session_keys_change_cb,
-    const media::SessionExpirationUpdateCB& session_expiration_update_cb) {
+    const media::SessionExpirationUpdateCB& session_expiration_update_cb,
+    const CdmCreatedCB& cdm_created_cb) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (!security_origin.is_valid())
-    return nullptr;
+  if (!security_origin.is_valid()) {
+    base::MessageLoopProxy::current()->PostTask(
+        FROM_HERE, base::Bind(cdm_created_cb, nullptr));
+    return;
+  }
+
+  scoped_ptr<media::MediaKeys> cdm;
 
   if (media::CanUseAesDecryptor(key_system)) {
     // TODO(sandersd): Currently the prefixed API always allows distinctive
     // identifiers and persistent state. Once that changes we can sanity check
     // here that neither is allowed for AesDecryptor, since it does not support
     // them and should never be configured that way. http://crbug.com/455271
-    return scoped_ptr<media::MediaKeys>(
-        new media::AesDecryptor(security_origin, session_message_cb,
-                                session_closed_cb, session_keys_change_cb));
+    cdm.reset(new media::AesDecryptor(security_origin, session_message_cb,
+                                      session_closed_cb,
+                                      session_keys_change_cb));
+  } else {
+#if defined(ENABLE_PEPPER_CDMS)
+    cdm = PpapiDecryptor::Create(
+        key_system, allow_distinctive_identifier, allow_persistent_state,
+        security_origin, create_pepper_cdm_cb_, session_message_cb,
+        session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
+        session_expiration_update_cb);
+#elif defined(ENABLE_BROWSER_CDMS)
+    DCHECK(allow_distinctive_identifier);
+    DCHECK(allow_persistent_state);
+    cdm = ProxyMediaKeys::Create(
+        key_system, security_origin, manager_, session_message_cb,
+        session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
+        session_expiration_update_cb);
+#endif  // defined(ENABLE_PEPPER_CDMS)
   }
 
-#if defined(ENABLE_PEPPER_CDMS)
-  return scoped_ptr<media::MediaKeys>(PpapiDecryptor::Create(
-      key_system, allow_distinctive_identifier, allow_persistent_state,
-      security_origin, create_pepper_cdm_cb_, session_message_cb,
-      session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
-      session_expiration_update_cb));
-#elif defined(ENABLE_BROWSER_CDMS)
-  DCHECK(allow_distinctive_identifier);
-  DCHECK(allow_persistent_state);
-  return scoped_ptr<media::MediaKeys>(ProxyMediaKeys::Create(
-      key_system, security_origin, manager_, session_message_cb,
-      session_closed_cb, legacy_session_error_cb, session_keys_change_cb,
-      session_expiration_update_cb));
-#else
-  return nullptr;
-#endif  // defined(ENABLE_PEPPER_CDMS)
+  base::MessageLoopProxy::current()->PostTask(
+      FROM_HERE, base::Bind(cdm_created_cb, base::Passed(&cdm)));
 }
 
 }  // namespace content
diff --git a/content/renderer/media/crypto/render_cdm_factory.h b/content/renderer/media/crypto/render_cdm_factory.h
index f15bfe8a..af2d7ad 100644
--- a/content/renderer/media/crypto/render_cdm_factory.h
+++ b/content/renderer/media/crypto/render_cdm_factory.h
@@ -39,7 +39,8 @@
 
   ~RenderCdmFactory() override;
 
-  scoped_ptr<media::MediaKeys> Create(
+  // CdmFactory implementation.
+  void Create(
       const std::string& key_system,
       bool allow_distinctive_identifier,
       bool allow_persistent_state,
@@ -48,8 +49,8 @@
       const media::SessionClosedCB& session_closed_cb,
       const media::LegacySessionErrorCB& legacy_session_error_cb,
       const media::SessionKeysChangeCB& session_keys_change_cb,
-      const media::SessionExpirationUpdateCB& session_expiration_update_cb)
-      override;
+      const media::SessionExpirationUpdateCB& session_expiration_update_cb,
+      const CdmCreatedCB& cdm_created_cb) override;
 
  private:
 #if defined(ENABLE_PEPPER_CDMS)
diff --git a/content/renderer/media/rtc_video_encoder.cc b/content/renderer/media/rtc_video_encoder.cc
index 44c9107..82e61f6 100644
--- a/content/renderer/media/rtc_video_encoder.cc
+++ b/content/renderer/media/rtc_video_encoder.cc
@@ -18,6 +18,7 @@
 #include "media/filters/h264_parser.h"
 #include "media/renderers/gpu_video_accelerator_factories.h"
 #include "media/video/video_encode_accelerator.h"
+#include "third_party/libyuv/include/libyuv.h"
 #include "third_party/webrtc/system_wrappers/interface/tick_util.h"
 
 #define NOTIFY_ERROR(x)                             \
@@ -514,18 +515,24 @@
   // Do a strided copy of the input frame to match the input requirements for
   // the encoder.
   // TODO(sheu): support zero-copy from WebRTC.  http://crbug.com/269312
-  media::CopyYPlane(next_frame->buffer(webrtc::kYPlane),
-                    next_frame->stride(webrtc::kYPlane),
-                    next_frame->height(),
-                    frame.get());
-  media::CopyUPlane(next_frame->buffer(webrtc::kUPlane),
-                    next_frame->stride(webrtc::kUPlane),
-                    next_frame->height(),
-                    frame.get());
-  media::CopyVPlane(next_frame->buffer(webrtc::kVPlane),
-                    next_frame->stride(webrtc::kVPlane),
-                    next_frame->height(),
-                    frame.get());
+  if (libyuv::I420Copy(next_frame->buffer(webrtc::kYPlane),
+                       next_frame->stride(webrtc::kYPlane),
+                       next_frame->buffer(webrtc::kUPlane),
+                       next_frame->stride(webrtc::kUPlane),
+                       next_frame->buffer(webrtc::kVPlane),
+                       next_frame->stride(webrtc::kVPlane),
+                       frame->data(media::VideoFrame::kYPlane),
+                       frame->stride(media::VideoFrame::kYPlane),
+                       frame->data(media::VideoFrame::kUPlane),
+                       frame->stride(media::VideoFrame::kUPlane),
+                       frame->data(media::VideoFrame::kVPlane),
+                       frame->stride(media::VideoFrame::kVPlane),
+                       next_frame->width(),
+                       next_frame->height())) {
+    DLOG(ERROR) << "Failed to copy buffer";
+    NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
+    return;
+  }
 
   video_encoder_->Encode(frame, next_frame_keyframe);
   input_buffers_free_.pop_back();
diff --git a/content/renderer/media/webrtc/DEPS b/content/renderer/media/webrtc/DEPS
deleted file mode 100644
index 9d1e386..0000000
--- a/content/renderer/media/webrtc/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
-  # For video cropping and scaling.
-  "+third_party/libyuv",
-]
-
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 2d94098d..d9f9d58 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1263,7 +1263,7 @@
     WebDocument document = element.document();
     bool is_fullscreen_element = (element == document.fullScreenElement());
     if (!view_data_.is_fullscreen && desired_fullscreen_state_ &&
-        render_frame()->GetRenderWidget()->is_fullscreen() &&
+        render_frame()->GetRenderWidget()->is_fullscreen_granted() &&
         is_fullscreen_element) {
       // Entered fullscreen. Only possible via SetFullscreen().
       view_data_.is_fullscreen = true;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 93029e3..bf54e6d 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1049,6 +1049,8 @@
                         OnAddStyleSheetByURL)
     IPC_MESSAGE_HANDLER(FrameMsg_SetAccessibilityMode,
                         OnSetAccessibilityMode)
+    IPC_MESSAGE_HANDLER(AccessibilityMsg_SnapshotTree,
+                        OnSnapshotAccessibilityTree)
     IPC_MESSAGE_HANDLER(FrameMsg_DisownOpener, OnDisownOpener)
     IPC_MESSAGE_HANDLER(FrameMsg_CommitNavigation, OnCommitNavigation)
     IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags)
@@ -1579,6 +1581,13 @@
     renderer_accessibility_ = new RendererAccessibility(this);
 }
 
+void RenderFrameImpl::OnSnapshotAccessibilityTree(int callback_id) {
+  ui::AXTreeUpdate response;
+  RendererAccessibility::SnapshotAccessibilityTree(this, &response);
+  Send(new AccessibilityHostMsg_SnapshotResponse(
+      routing_id_, callback_id, response));
+}
+
 void RenderFrameImpl::OnDisownOpener() {
   // TODO(creis): We should only see this for main frames for now.  To support
   // disowning the opener on subframes, we will need to move WebContentsImpl's
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index b08a73c..92e32e35 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -643,6 +643,7 @@
   void OnHideTransitionElements(const std::string& css_selector);
   void OnShowTransitionElements(const std::string& css_selector);
   void OnSetAccessibilityMode(AccessibilityMode new_mode);
+  void OnSnapshotAccessibilityTree(int callback_id);
   void OnDisownOpener();
   void OnDidUpdateSandboxFlags(SandboxFlags flags);
   void OnTextTrackSettingsChanged(
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 7aeb278..633c18a6 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -125,7 +125,7 @@
   resize_params.top_controls_height = 0.f;
   resize_params.top_controls_shrink_blink_size = false;
   resize_params.resizer_rect = gfx::Rect();
-  resize_params.is_fullscreen = false;
+  resize_params.is_fullscreen_granted = false;
 
   scoped_ptr<IPC::Message> resize_message(new ViewMsg_Resize(0, resize_params));
   FrameWidget()->OnMessageReceived(*resize_message);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 1f99730b..9a4a22c 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2687,7 +2687,7 @@
            top_controls_height_,
            visible_viewport_size_,
            resizer_rect_,
-           is_fullscreen_,
+           is_fullscreen_granted_,
            NO_RESIZE_ACK);
   }
 }
@@ -3695,7 +3695,7 @@
   params.top_controls_shrink_blink_size = false;
   params.top_controls_height = 0.f;
   params.resizer_rect = WebRect();
-  params.is_fullscreen = is_fullscreen();
+  params.is_fullscreen_granted = is_fullscreen_granted();
   OnResize(params);
 }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index d4792958..1b6be53 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -269,7 +269,7 @@
   void Apply(bool top_controls_shrink_blink_size,
              float top_controls_height,
              gfx::Rect resizer_rect,
-             bool is_fullscreen);
+             bool is_fullscreen_granted);
 
   RenderWidget* widget_;
 
@@ -307,7 +307,7 @@
   Apply(widget_->top_controls_shrink_blink_size_,
         widget_->top_controls_height_,
         widget_->resizer_rect_,
-        widget_->is_fullscreen_);
+        widget_->is_fullscreen_granted_);
 }
 
 RenderWidget::ScreenMetricsEmulator::~ScreenMetricsEmulator() {
@@ -323,7 +323,7 @@
                   widget_->top_controls_height_,
                   original_visible_viewport_size_,
                   widget_->resizer_rect_,
-                  widget_->is_fullscreen_,
+                  widget_->is_fullscreen_granted_,
                   NO_RESIZE_ACK);
 }
 
@@ -337,14 +337,14 @@
   Apply(widget_->top_controls_shrink_blink_size_,
         widget_->top_controls_height_,
         widget_->resizer_rect_,
-        widget_->is_fullscreen_);
+        widget_->is_fullscreen_granted_);
 }
 
 void RenderWidget::ScreenMetricsEmulator::Apply(
     bool top_controls_shrink_blink_size,
     float top_controls_height,
     gfx::Rect resizer_rect,
-    bool is_fullscreen) {
+    bool is_fullscreen_granted) {
   applied_widget_rect_.set_size(gfx::Size(params_.viewSize));
   if (!applied_widget_rect_.width())
     applied_widget_rect_.set_width(original_size_.width());
@@ -409,7 +409,7 @@
                   top_controls_height,
                   applied_widget_rect_.size(),
                   resizer_rect,
-                  is_fullscreen,
+                  is_fullscreen_granted,
                   NO_RESIZE_ACK);
 }
 
@@ -424,7 +424,7 @@
   Apply(params.top_controls_shrink_blink_size,
         params.top_controls_height,
         params.resizer_rect,
-        params.is_fullscreen);
+        params.is_fullscreen_granted);
 
   if (need_ack) {
     widget_->set_next_paint_is_resize_ack();
@@ -479,7 +479,7 @@
       did_show_(false),
       is_hidden_(hidden),
       never_visible_(never_visible),
-      is_fullscreen_(false),
+      is_fullscreen_granted_(false),
       has_focus_(false),
       handling_input_event_(false),
       handling_ime_event_(false),
@@ -771,7 +771,7 @@
                           float top_controls_height,
                           const gfx::Size& visible_viewport_size,
                           const gfx::Rect& resizer_rect,
-                          bool is_fullscreen,
+                          bool is_fullscreen_granted,
                           const ResizeAck resize_ack) {
   if (resizing_mode_selector_->NeverUsesSynchronousResize()) {
     // A resize ack shouldn't be requested if we have not ACK'd the previous
@@ -795,10 +795,10 @@
   resizer_rect_ = resizer_rect;
 
   // NOTE: We may have entered fullscreen mode without changing our size.
-  bool fullscreen_change = is_fullscreen_ != is_fullscreen;
+  bool fullscreen_change = is_fullscreen_granted_ != is_fullscreen_granted;
   if (fullscreen_change)
     WillToggleFullscreen();
-  is_fullscreen_ = is_fullscreen;
+  is_fullscreen_granted_ = is_fullscreen_granted;
 
   webwidget_->setTopControlsHeight(top_controls_height,
                                    top_controls_shrink_blink_size_);
@@ -843,7 +843,7 @@
          top_controls_height_,
          new_window_rect.size(),
          gfx::Rect(),
-         is_fullscreen_,
+         is_fullscreen_granted_,
          NO_RESIZE_ACK);
   view_screen_rect_ = new_window_rect;
   window_screen_rect_ = new_window_rect;
@@ -903,7 +903,7 @@
          params.top_controls_height,
          params.visible_viewport_size,
          params.resizer_rect,
-         params.is_fullscreen,
+         params.is_fullscreen_granted,
          params.needs_resize_ack ? SEND_RESIZE_ACK : NO_RESIZE_ACK);
 
   if (orientation_changed)
@@ -1841,7 +1841,7 @@
   if (!webwidget_)
     return;
 
-  if (is_fullscreen_) {
+  if (is_fullscreen_granted_) {
     webwidget_->willExitFullScreen();
   } else {
     webwidget_->willEnterFullScreen();
@@ -1852,7 +1852,7 @@
   if (!webwidget_)
     return;
 
-  if (is_fullscreen_) {
+  if (is_fullscreen_granted_) {
     webwidget_->didEnterFullScreen();
   } else {
     webwidget_->didExitFullScreen();
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 072da8c..6802d64e0 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -123,7 +123,7 @@
   blink::WebWidget* webwidget() const { return webwidget_; }
   gfx::Size size() const { return size_; }
   bool has_focus() const { return has_focus_; }
-  bool is_fullscreen() const { return is_fullscreen_; }
+  bool is_fullscreen_granted() const { return is_fullscreen_granted_; }
   bool is_hidden() const { return is_hidden_; }
   bool handling_input_event() const { return handling_input_event_; }
   // Temporary for debugging purposes...
@@ -389,7 +389,7 @@
               float top_controls_height,
               const gfx::Size& visible_viewport_size,
               const gfx::Rect& resizer_rect,
-              bool is_fullscreen,
+              bool is_fullscreen_granted,
               ResizeAck resize_ack);
   // Used to force the size of a window when running layout tests.
   void SetWindowRectSynchronously(const gfx::Rect& new_window_rect);
@@ -659,8 +659,8 @@
   // Indicates that we are never visible, so never produce graphical output.
   bool never_visible_;
 
-  // Indicates that we are in fullscreen mode.
-  bool is_fullscreen_;
+  // Indicates whether tab-initiated fullscreen was granted.
+  bool is_fullscreen_granted_;
 
   // Indicates whether we have been focused/unfocused by the browser.
   bool has_focus_;
diff --git a/content/renderer/render_widget_browsertest.cc b/content/renderer/render_widget_browsertest.cc
index b0ed636..ab314272 100644
--- a/content/renderer/render_widget_browsertest.cc
+++ b/content/renderer/render_widget_browsertest.cc
@@ -18,7 +18,7 @@
   resize_params.top_controls_height = 0.f;
   resize_params.top_controls_shrink_blink_size = false;
   resize_params.resizer_rect = gfx::Rect();
-  resize_params.is_fullscreen = false;
+  resize_params.is_fullscreen_granted = false;
   resize_params.needs_resize_ack = false;
   OnResize(resize_params);
   EXPECT_EQ(resize_params.needs_resize_ack, next_paint_is_resize_ack());
diff --git a/content/renderer/resizing_mode_selector.cc b/content/renderer/resizing_mode_selector.cc
index d51b457..08a1cf4 100644
--- a/content/renderer/resizing_mode_selector.cc
+++ b/content/renderer/resizing_mode_selector.cc
@@ -22,7 +22,7 @@
     RenderWidget* widget,
     const ViewMsg_Resize_Params& params) {
   return is_synchronous_mode_ &&
-      params.is_fullscreen == widget->is_fullscreen() &&
+      params.is_fullscreen_granted == widget->is_fullscreen_granted() &&
       params.screen_info.deviceScaleFactor ==
         widget->screenInfo().deviceScaleFactor;
 }
diff --git a/content/shell/browser/layout_test/layout_test_devtools_frontend.cc b/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
index 64d8035..25ec42f8 100644
--- a/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
+++ b/content/shell/browser/layout_test/layout_test_devtools_frontend.cc
@@ -62,6 +62,7 @@
 void LayoutTestDevToolsFrontend::ReuseFrontend(const std::string& settings,
                                                const std::string frontend_url) {
   DisconnectFromTarget();
+  preferences()->Clear();
   frontend_shell()->LoadURL(GetDevToolsPathAsURL(settings, frontend_url));
 }
 
diff --git a/content/shell/browser/shell_devtools_frontend.cc b/content/shell/browser/shell_devtools_frontend.cc
index a80490d..6d8e163 100644
--- a/content/shell/browser/shell_devtools_frontend.cc
+++ b/content/shell/browser/shell_devtools_frontend.cc
@@ -224,6 +224,22 @@
         new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
     fetcher->Start();
     return;
+  } else if (method == "getPreferences") {
+    SendMessageAck(request_id, &preferences_);
+    return;
+  } else if (method == "setPreference") {
+    std::string name;
+    std::string value;
+    if (!params->GetString(0, &name) ||
+        !params->GetString(1, &value)) {
+      return;
+    }
+    preferences_.SetStringWithoutPathExpansion(name, value);
+  } else if (method == "removePreference") {
+    std::string name;
+    if (!params->GetString(0, &name))
+      return;
+    preferences_.RemoveWithoutPathExpansion(name, nullptr);
   } else {
     return;
   }
diff --git a/content/shell/browser/shell_devtools_frontend.h b/content/shell/browser/shell_devtools_frontend.h
index d6ce91b..15a236e 100644
--- a/content/shell/browser/shell_devtools_frontend.h
+++ b/content/shell/browser/shell_devtools_frontend.h
@@ -10,6 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_frontend_host.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -54,6 +55,7 @@
   void AgentHostClosed(DevToolsAgentHost* agent_host, bool replaced) override;
   void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
                                const std::string& message) override;
+  base::DictionaryValue* preferences() { return &preferences_; }
 
  private:
   // WebContentsObserver overrides
@@ -78,6 +80,7 @@
   scoped_ptr<DevToolsFrontendHost> frontend_host_;
   using PendingRequestsMap = std::map<const net::URLFetcher*, int>;
   PendingRequestsMap pending_requests_;
+  base::DictionaryValue preferences_;
   base::WeakPtrFactory<ShellDevToolsFrontend> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellDevToolsFrontend);
diff --git a/content/shell/renderer/test_runner/test_interfaces.cc b/content/shell/renderer/test_runner/test_interfaces.cc
index 6b88763a..a70ff059 100644
--- a/content/shell/renderer/test_runner/test_interfaces.cc
+++ b/content/shell/renderer/test_runner/test_interfaces.cc
@@ -7,6 +7,8 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "content/shell/common/shell_switches.h"
@@ -118,22 +120,12 @@
     test_runner_->ClearDevToolsLocalStorage();
   if (spec.find("/inspector/") != std::string::npos) {
     // Subfolder name determines default panel to open.
-    std::string settings = "";
     std::string test_path = spec.substr(spec.find("/inspector/") + 11);
-    size_t slash_index = test_path.find("/");
-    std::string test_path_setting = base::StringPrintf(
-        "\"testPath\":\"\\\"%s\\\"\"", spec.c_str());
-
-    // TODO(pfeldman): remove once migrated to testPath.
-    std::string last_active_panel;
-    if (slash_index != std::string::npos) {
-      last_active_panel = base::StringPrintf(
-          ",\"lastActivePanel\":\"\\\"%s\\\"\"",
-          test_path.substr(0, slash_index).c_str());
-    }
-
-    test_runner_->ShowDevTools(base::StringPrintf("{%s%s}",
-        test_path_setting.c_str(), last_active_panel.c_str()), std::string());
+    base::DictionaryValue settings;
+    settings.SetString("testPath", base::GetQuotedJSONString(spec));
+    std::string settings_string;
+    base::JSONWriter::Write(&settings, &settings_string);
+    test_runner_->ShowDevTools(settings_string, std::string());
   }
   if (spec.find("/viewsource/") != std::string::npos) {
     test_runner_->setShouldEnableViewSource(true);
diff --git a/content/shell/renderer/test_runner/web_ax_object_proxy.cc b/content/shell/renderer/test_runner/web_ax_object_proxy.cc
index d139fda0..019f6415 100644
--- a/content/shell/renderer/test_runner/web_ax_object_proxy.cc
+++ b/content/shell/renderer/test_runner/web_ax_object_proxy.cc
@@ -488,6 +488,10 @@
       .SetProperty("childrenCount", &WebAXObjectProxy::ChildrenCount)
       .SetProperty("selectionStart", &WebAXObjectProxy::SelectionStart)
       .SetProperty("selectionEnd", &WebAXObjectProxy::SelectionEnd)
+      .SetProperty("selectionStartLineNumber",
+                   &WebAXObjectProxy::SelectionStartLineNumber)
+      .SetProperty("selectionEndLineNumber",
+                   &WebAXObjectProxy::SelectionEndLineNumber)
       .SetProperty("insertionPointLineNumber",
                    &WebAXObjectProxy::InsertionPointLineNumber)
       .SetProperty("selectedTextRange", &WebAXObjectProxy::SelectedTextRange)
@@ -727,6 +731,17 @@
   return accessibility_object_.selectionEnd();
 }
 
+int WebAXObjectProxy::SelectionStartLineNumber() {
+  accessibility_object_.updateLayoutAndCheckValidity();
+  return accessibility_object_.selectionStartLineNumber();
+}
+
+int WebAXObjectProxy::SelectionEndLineNumber() {
+  accessibility_object_.updateLayoutAndCheckValidity();
+  return accessibility_object_.selectionEndLineNumber();
+}
+
+// TODO(nektar): Remove this function after updating tests.
 int WebAXObjectProxy::InsertionPointLineNumber() {
   accessibility_object_.updateLayoutAndCheckValidity();
   if (!accessibility_object_.isFocused())
@@ -734,6 +749,7 @@
   return accessibility_object_.selectionEndLineNumber();
 }
 
+// TODO(nektar): Remove this function after updating tests.
 std::string WebAXObjectProxy::SelectedTextRange() {
   accessibility_object_.updateLayoutAndCheckValidity();
   unsigned selection_start = accessibility_object_.selectionStart();
@@ -1174,7 +1190,7 @@
 
 std::string WebAXObjectProxy::NameFrom() {
   accessibility_object_.updateLayoutAndCheckValidity();
-  blink::WebAXNameFrom nameFrom;
+  blink::WebAXNameFrom nameFrom = blink::WebAXNameFromContents;
   blink::WebVector<blink::WebAXObject> nameObjects;
   accessibility_object_.name(nameFrom, nameObjects);
   switch(nameFrom) {
diff --git a/content/shell/renderer/test_runner/web_ax_object_proxy.h b/content/shell/renderer/test_runner/web_ax_object_proxy.h
index 9a48d49..b734296 100644
--- a/content/shell/renderer/test_runner/web_ax_object_proxy.h
+++ b/content/shell/renderer/test_runner/web_ax_object_proxy.h
@@ -71,7 +71,11 @@
   int ChildrenCount();
   int SelectionStart();
   int SelectionEnd();
+  int SelectionStartLineNumber();
+  int SelectionEndLineNumber();
+  // TODO(nektar): Remove this function after updating tests.
   int InsertionPointLineNumber();
+  // TODO(nektar): Remove this function after updating tests.
   std::string SelectedTextRange();
   bool IsEnabled();
   bool IsRequired();
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index 21a33d0..9097a895 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -4,8 +4,15 @@
 
 #include "content/test/content_browser_test_utils_internal.h"
 
+#include <algorithm>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/strings/stringprintf.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/test/test_frame_navigation_observer.h"
 #include "url/gurl.h"
 
@@ -20,4 +27,196 @@
   observer.Wait();
 }
 
+FrameTreeVisualizer::FrameTreeVisualizer() {
+}
+
+FrameTreeVisualizer::~FrameTreeVisualizer() {
+}
+
+std::string FrameTreeVisualizer::DepictFrameTree(FrameTreeNode* root) {
+  // Tracks the sites actually used in this depiction.
+  std::map<std::string, SiteInstance*> legend;
+
+  // Traversal 1: Assign names to current frames. This ensures that the first
+  // call to the pretty-printer will result in a naming of the site instances
+  // that feels natural and stable.
+  std::stack<FrameTreeNode*> to_explore;
+  for (to_explore.push(root); !to_explore.empty();) {
+    FrameTreeNode* node = to_explore.top();
+    to_explore.pop();
+    for (size_t i = node->child_count(); i-- != 0;) {
+      to_explore.push(node->child_at(i));
+    }
+
+    RenderFrameHost* current = node->render_manager()->current_frame_host();
+    legend[GetName(current->GetSiteInstance())] = current->GetSiteInstance();
+  }
+
+  // Traversal 2: Assign names to the pending/speculative frames. For stability
+  // of assigned names it's important to do this before trying to name the
+  // proxies, which have a less well defined order.
+  for (to_explore.push(root); !to_explore.empty();) {
+    FrameTreeNode* node = to_explore.top();
+    to_explore.pop();
+    for (size_t i = node->child_count(); i-- != 0;) {
+      to_explore.push(node->child_at(i));
+    }
+
+    RenderFrameHost* pending = node->render_manager()->pending_frame_host();
+    RenderFrameHost* spec =
+        node->render_manager()->speculative_render_frame_host_.get();
+    if (pending)
+      legend[GetName(pending->GetSiteInstance())] = pending->GetSiteInstance();
+    if (spec)
+      legend[GetName(spec->GetSiteInstance())] = spec->GetSiteInstance();
+  }
+
+  // Traversal 3: Assign names to the proxies and add them to |legend| too.
+  // Typically, only openers should have their names assigned this way.
+  for (to_explore.push(root); !to_explore.empty();) {
+    FrameTreeNode* node = to_explore.top();
+    to_explore.pop();
+    for (size_t i = node->child_count(); i-- != 0;) {
+      to_explore.push(node->child_at(i));
+    }
+
+    // Sort the proxies by SiteInstance ID to avoid hash_map ordering.
+    std::map<int, RenderFrameProxyHost*> sorted_proxy_hosts;
+    for (auto& proxy_pair : node->render_manager()->proxy_hosts_) {
+      sorted_proxy_hosts.insert(proxy_pair);
+    }
+    for (auto& proxy_pair : sorted_proxy_hosts) {
+      RenderFrameProxyHost* proxy = proxy_pair.second;
+      legend[GetName(proxy->GetSiteInstance())] = proxy->GetSiteInstance();
+    }
+  }
+
+  // Traversal 4: Now that all names are assigned, make a big loop to pretty-
+  // print the tree. Each iteration produces exactly one line of format.
+  std::string result;
+  for (to_explore.push(root); !to_explore.empty();) {
+    FrameTreeNode* node = to_explore.top();
+    to_explore.pop();
+    for (size_t i = node->child_count(); i-- != 0;) {
+      to_explore.push(node->child_at(i));
+    }
+
+    // Draw the feeler line tree graphics by walking up to the root. A feeler
+    // line is needed for each ancestor that is the last child of its parent.
+    // This creates the ASCII art that looks like:
+    //    Foo
+    //      |--Foo
+    //      |--Foo
+    //      |    |--Foo
+    //      |    +--Foo
+    //      |         +--Foo
+    //      +--Foo
+    //           +--Foo
+    //
+    // TODO(nick): Make this more elegant.
+    std::string line;
+    if (node != root) {
+      if (node->parent()->child_at(node->parent()->child_count() - 1) != node)
+        line = "  |--";
+      else
+        line = "  +--";
+      for (FrameTreeNode* up = node->parent(); up != root; up = up->parent()) {
+        if (up->parent()->child_at(up->parent()->child_count() - 1) != up)
+          line = "  |  " + line;
+        else
+          line = "     " + line;
+      }
+    }
+
+    // Prefix one extra space of padding for two reasons. First, this helps the
+    // diagram aligns nicely with the legend. Second, this makes it easier to
+    // read the diffs that gtest spits out on EXPECT_EQ failure.
+    line = " " + line;
+
+    // Summarize the FrameTreeNode's state. Always show the site of the current
+    // RenderFrameHost, and show any exceptional state of the node, like a
+    // pending or speculative RenderFrameHost.
+    RenderFrameHost* current = node->render_manager()->current_frame_host();
+    RenderFrameHost* pending = node->render_manager()->pending_frame_host();
+    RenderFrameHost* spec =
+        node->render_manager()->speculative_render_frame_host_.get();
+    base::StringAppendF(&line, "Site %s",
+                        GetName(current->GetSiteInstance()).c_str());
+    if (pending) {
+      base::StringAppendF(&line, " (%s pending)",
+                          GetName(pending->GetSiteInstance()).c_str());
+    }
+    if (spec) {
+      base::StringAppendF(&line, " (%s speculative)",
+                          GetName(spec->GetSiteInstance()).c_str());
+    }
+
+    // Show the SiteInstances of the RenderFrameProxyHosts of this node.
+    if (!node->render_manager()->proxy_hosts_.empty()) {
+      // Show a dashed line of variable length before the proxy list. Always at
+      // least two dashes.
+      line.append(" --");
+
+      // To make proxy lists align vertically for the first three tree levels,
+      // pad with dashes up to a first tab stop at column 19 (which works out to
+      // text editor column 28 in the typical diagram fed to EXPECT_EQ as a
+      // string literal). Lining the lists up vertically makes differences in
+      // the proxy sets easier to spot visually. We choose not to use the
+      // *actual* tree height here, because that would make the diagram's
+      // appearance less stable as the tree's shape evolves.
+      while (line.length() < 20) {
+        line.append("-");
+      }
+      line.append(" proxies for");
+
+      // Sort these alphabetically, to avoid hash_map ordering dependency.
+      std::vector<std::string> sorted_proxy_hosts;
+      for (auto& proxy_pair : node->render_manager()->proxy_hosts_) {
+        sorted_proxy_hosts.push_back(
+            GetName(proxy_pair.second->GetSiteInstance()));
+      }
+      std::sort(sorted_proxy_hosts.begin(), sorted_proxy_hosts.end());
+      for (std::string& proxy_name : sorted_proxy_hosts) {
+        base::StringAppendF(&line, " %s", proxy_name.c_str());
+      }
+    }
+    if (node != root)
+      result.append("\n");
+    result.append(line);
+  }
+
+  // Finally, show a legend with details of the site instances.
+  const char* prefix = "Where ";
+  for (auto& legend_entry : legend) {
+    SiteInstanceImpl* site_instance =
+        static_cast<SiteInstanceImpl*>(legend_entry.second);
+    base::StringAppendF(&result, "\n%s%s = %s", prefix,
+                        legend_entry.first.c_str(),
+                        site_instance->GetSiteURL().spec().c_str());
+    // Highlight some exceptionable conditions.
+    if (site_instance->active_frame_count() == 0)
+      result.append(" (active_frame_count == 0)");
+    if (!site_instance->GetProcess()->HasConnection())
+      result.append(" (no process)");
+    prefix = "      ";
+  }
+  return result;
+}
+
+std::string FrameTreeVisualizer::GetName(SiteInstance* site_instance) {
+  // Indices into the vector correspond to letters of the alphabet.
+  size_t index =
+      std::find(seen_site_instance_ids_.begin(), seen_site_instance_ids_.end(),
+                site_instance->GetId()) -
+      seen_site_instance_ids_.begin();
+  if (index == seen_site_instance_ids_.size())
+    seen_site_instance_ids_.push_back(site_instance->GetId());
+
+  // Whosoever writes a test using >=26 site instances shall be a lucky ducky.
+  if (index < 25)
+    return base::StringPrintf("%c", 'A' + static_cast<char>(index));
+  else
+    return base::StringPrintf("Z%d", static_cast<int>(index - 25));
+}
+
 }  // namespace content
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index a27aea58..4486d14 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -5,21 +5,66 @@
 #ifndef CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
 #define CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
 
-// A collections of functions designed for use with content_shell based browser
+// A collection of functions designed for use with content_shell based browser
 // tests internal to the content/ module.
 // Note: If a function here also works with browser_tests, it should be in
 // the content public API.
 
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
 class GURL;
 
 namespace content {
 
 class FrameTreeNode;
+class SiteInstance;
 
 // Navigates the frame represented by |node| to |url|, blocking until the
 // navigation finishes.
 void NavigateFrameToURL(FrameTreeNode* node, const GURL& url);
 
+// Creates compact textual representations of the state of the frame tree that
+// is appropriate for use in assertions.
+//
+// The diagrams show frame tree structure, the SiteInstance of current frames,
+// presence of pending frames, and the SiteInstances of any and all proxies.
+// They look like this:
+//
+//        Site A (D pending) -- proxies for B C
+//          |--Site B --------- proxies for A C
+//          +--Site C --------- proxies for B A
+//               |--Site A ---- proxies for B
+//               +--Site A ---- proxies for B
+//                    +--Site A -- proxies for B
+//       Where A = http://127.0.0.1/
+//             B = http://foo.com/ (no process)
+//             C = http://bar.com/
+//             D = http://next.com/
+//
+// SiteInstances are assigned single-letter names (A, B, C) which are remembered
+// across invocations of the pretty-printer.
+class FrameTreeVisualizer {
+ public:
+  FrameTreeVisualizer();
+  ~FrameTreeVisualizer();
+
+  // Formats and returns a diagram for the provided FrameTreeNode.
+  std::string DepictFrameTree(FrameTreeNode* root);
+
+ private:
+  // Assign or retrive the abbreviated short name (A, B, C) for a site instance.
+  std::string GetName(SiteInstance* site_instance);
+
+  // Elements are site instance ids. The index of the SiteInstance in the vector
+  // determines the abbreviated name (0->A, 1->B) for that SiteInstance.
+  std::vector<int> seen_site_instance_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameTreeVisualizer);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
diff --git a/content/test/data/post_message.html b/content/test/data/post_message.html
index e111fea..fdec3e3 100644
--- a/content/test/data/post_message.html
+++ b/content/test/data/post_message.html
@@ -34,6 +34,12 @@
     return true;
   }
 
+  function registerUnload() {
+    window.addEventListener('unload', function(e) {
+	postToParent("message-from-unload", "*");
+    });
+  }
+
   // Listen to incoming messages.
   var receivedMessages = 0;
   var receivedMessagesWithPort = 0;
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index 5380c93..2ffee6c9 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -249,8 +249,8 @@
   return render_view_created_;
 }
 
-bool TestRenderViewHost::IsFullscreen() const {
-  return RenderViewHostImpl::IsFullscreen();
+bool TestRenderViewHost::IsFullscreenGranted() const {
+  return RenderViewHostImpl::IsFullscreenGranted();
 }
 
 void TestRenderViewHost::SimulateWasHidden() {
diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h
index c7a75a3..9ef744ff 100644
--- a/content/test/test_render_view_host.h
+++ b/content/test/test_render_view_host.h
@@ -241,7 +241,7 @@
                         int32 max_page_id,
                         bool window_was_created_with_opener) override;
   bool IsRenderViewLive() const override;
-  bool IsFullscreen() const override;
+  bool IsFullscreenGranted() const override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, FilterNavigate);
diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc
index 5944f87..5a84dec6 100644
--- a/content/zygote/zygote_linux.cc
+++ b/content/zygote/zygote_linux.cc
@@ -36,6 +36,8 @@
 #include "content/public/common/zygote_fork_delegate_linux.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_switches.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
 
 // See http://code.google.com/p/chromium/wiki/LinuxZygote
 
@@ -47,6 +49,14 @@
 void SIGCHLDHandler(int signal) {
 }
 
+// On Linux, when a process is the init process of a PID namespace, it cannot be
+// terminated by signals like SIGTERM or SIGINT, since they are ignored unless
+// we register a handler for them. In the handlers, we exit with this special
+// exit code that GetTerminationStatus understands to mean that we were
+// terminated by an external signal.
+const int kKilledExitCode = 0x80;
+const int kUnexpectedExitCode = 0x81;
+
 int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) {
   for (size_t index = 0; index < fd_mapping.size(); ++index) {
     if (fd_mapping[index].key == key)
@@ -104,7 +114,7 @@
   struct sigaction action;
   memset(&action, 0, sizeof(action));
   action.sa_handler = &SIGCHLDHandler;
-  CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+  PCHECK(sigaction(SIGCHLD, &action, NULL) == 0);
 
   if (UsingSUIDSandbox() || UsingNSSandbox()) {
     // Let the ZygoteHost know we are ready to go.
@@ -305,6 +315,11 @@
     // Time to forget about this process.
     process_info_map_.erase(real_pid);
   }
+
+  if (WIFEXITED(*exit_code) && WEXITSTATUS(*exit_code) == kKilledExitCode) {
+    *status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+  }
+
   return true;
 }
 
@@ -375,12 +390,33 @@
     CHECK_NE(pid, 0);
   } else {
     CreatePipe(&read_pipe, &write_pipe);
-    // This is roughly equivalent to a fork(). We are using ForkWithFlags mainly
-    // to give it some more diverse test coverage.
-    pid = base::ForkWithFlags(SIGCHLD, nullptr, nullptr);
+    if (sandbox_flags_ & kSandboxLinuxPIDNS &&
+        sandbox_flags_ & kSandboxLinuxUserNS) {
+      pid = sandbox::NamespaceSandbox::ForkInNewPidNamespace(
+          /*drop_capabilities_in_child=*/true);
+    } else {
+      pid = fork();
+    }
   }
 
   if (pid == 0) {
+    // If the process is the init process inside a PID namespace, it must have
+    // explicit signal handlers.
+    if (getpid() == 1) {
+      for (const int sig : {SIGINT, SIGTERM}) {
+        sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
+            sig, kKilledExitCode);
+      }
+
+      static const int kUnexpectedSignals[] = {
+          SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, SIGUSR1, SIGUSR2,
+      };
+      for (const int sig : kUnexpectedSignals) {
+        sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
+            sig, kUnexpectedExitCode);
+      }
+    }
+
     // In the child process.
     write_pipe.reset();
 
diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc
index 8a03e4e..ab12e5b 100644
--- a/content/zygote/zygote_main_linux.cc
+++ b/content/zygote/zygote_main_linux.cc
@@ -40,6 +40,7 @@
 #include "content/public/common/zygote_fork_delegate_linux.h"
 #include "content/zygote/zygote_linux.h"
 #include "crypto/nss_util.h"
+#include "sandbox/linux/services/credentials.h"
 #include "sandbox/linux/services/init_process_reaper.h"
 #include "sandbox/linux/services/libc_urandom_override.h"
 #include "sandbox/linux/services/namespace_sandbox.h"
@@ -80,6 +81,11 @@
   }
 }
 
+void RunTwoClosures(const base::Closure* first, const base::Closure* second) {
+  first->Run();
+  second->Run();
+}
+
 }  // namespace
 
 // See http://code.google.com/p/chromium/wiki/LinuxZygote
@@ -407,12 +413,20 @@
   return true;
 }
 
+static void DropAllCapabilities(int proc_fd) {
+  CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd));
+}
+
 static void EnterNamespaceSandbox(LinuxSandbox* linux_sandbox,
                                   base::Closure* post_fork_parent_callback) {
   linux_sandbox->EngageNamespaceSandbox();
 
   if (getpid() == 1) {
-    CHECK(CreateInitProcessReaper(post_fork_parent_callback));
+    base::Closure drop_all_caps_callback =
+        base::Bind(&DropAllCapabilities, linux_sandbox->proc_fd());
+    base::Closure callback = base::Bind(
+        &RunTwoClosures, &drop_all_caps_callback, post_fork_parent_callback);
+    CHECK(CreateInitProcessReaper(&callback));
   }
 }
 
diff --git a/device/BUILD.gn b/device/BUILD.gn
index bd83abe..3bf6fa7 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -20,6 +20,7 @@
     "bluetooth/bluetooth_chromeos_unittest.cc",
     "bluetooth/bluetooth_device_unittest.cc",
     "bluetooth/bluetooth_device_win_unittest.cc",
+    "bluetooth/bluetooth_discovery_filter_unittest.cc",
     "bluetooth/bluetooth_gatt_chromeos_unittest.cc",
     "bluetooth/bluetooth_low_energy_win_unittest.cc",
     "bluetooth/bluetooth_service_record_win_unittest.cc",
diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc
index 52c2d74..fd2e29b3 100644
--- a/device/bluetooth/bluetooth_adapter.cc
+++ b/device/bluetooth/bluetooth_adapter.cc
@@ -42,14 +42,70 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+void BluetoothAdapter::StartDiscoverySessionWithFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const DiscoverySessionCallback& callback,
+    const ErrorCallback& error_callback) {
+  AddDiscoverySession(discovery_filter.get(),
+                      base::Bind(&BluetoothAdapter::OnStartDiscoverySession,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 base::Passed(&discovery_filter), callback),
+                      error_callback);
+}
+
 void BluetoothAdapter::StartDiscoverySession(
     const DiscoverySessionCallback& callback,
     const ErrorCallback& error_callback) {
-  AddDiscoverySession(
-      base::Bind(&BluetoothAdapter::OnStartDiscoverySession,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 callback),
-      error_callback);
+  StartDiscoverySessionWithFilter(nullptr, callback, error_callback);
+}
+
+scoped_ptr<BluetoothDiscoveryFilter>
+BluetoothAdapter::GetMergedDiscoveryFilterHelper(
+    const BluetoothDiscoveryFilter* masked_filter,
+    bool omit) const {
+  scoped_ptr<BluetoothDiscoveryFilter> result;
+  bool first_merge = true;
+
+  std::set<BluetoothDiscoverySession*> temp(discovery_sessions_);
+  for (const auto& iter : temp) {
+    const BluetoothDiscoveryFilter* curr_filter = iter->GetDiscoveryFilter();
+
+    if (!iter->IsActive())
+      continue;
+
+    if (omit && curr_filter == masked_filter) {
+      // if masked_filter is pointing to empty filter, and there are
+      // multiple empty filters in discovery_sessions_, make sure we'll
+      // process next empty sessions.
+      omit = false;
+      continue;
+    }
+
+    if (first_merge) {
+      first_merge = false;
+      if (curr_filter) {
+        result.reset(new BluetoothDiscoveryFilter(
+            BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL));
+        result->CopyFrom(*curr_filter);
+      }
+      continue;
+    }
+
+    result = BluetoothDiscoveryFilter::Merge(result.get(), curr_filter);
+  }
+
+  return result.Pass();
+}
+
+scoped_ptr<BluetoothDiscoveryFilter>
+BluetoothAdapter::GetMergedDiscoveryFilter() const {
+  return GetMergedDiscoveryFilterHelper(nullptr, false);
+}
+
+scoped_ptr<BluetoothDiscoveryFilter>
+BluetoothAdapter::GetMergedDiscoveryFilterMasked(
+    BluetoothDiscoveryFilter* masked_filter) const {
+  return GetMergedDiscoveryFilterHelper(masked_filter, true);
 }
 
 BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() {
@@ -128,10 +184,12 @@
 }
 
 void BluetoothAdapter::OnStartDiscoverySession(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
     const DiscoverySessionCallback& callback) {
   VLOG(1) << "Discovery session started!";
   scoped_ptr<BluetoothDiscoverySession> discovery_session(
-      new BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter>(this)));
+      new BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter>(this),
+                                    discovery_filter.Pass()));
   discovery_sessions_.insert(discovery_session.get());
   callback.Run(discovery_session.Pass());
 }
diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h
index 77c64ee..d764ae97 100644
--- a/device/bluetooth/bluetooth_adapter.h
+++ b/device/bluetooth/bluetooth_adapter.h
@@ -20,6 +20,7 @@
 
 namespace device {
 
+class BluetoothDiscoveryFilter;
 class BluetoothDiscoverySession;
 class BluetoothGattCharacteristic;
 class BluetoothGattDescriptor;
@@ -273,6 +274,19 @@
       DiscoverySessionCallback;
   virtual void StartDiscoverySession(const DiscoverySessionCallback& callback,
                                      const ErrorCallback& error_callback);
+  virtual void StartDiscoverySessionWithFilter(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+      const DiscoverySessionCallback& callback,
+      const ErrorCallback& error_callback);
+
+  // Return all discovery filters assigned to this adapter merged together.
+  scoped_ptr<BluetoothDiscoveryFilter> GetMergedDiscoveryFilter() const;
+
+  // Works like GetMergedDiscoveryFilter, but doesn't take |masked_filter| into
+  // account. |masked_filter| is compared by pointer, and must be a member of
+  // active session.
+  scoped_ptr<BluetoothDiscoveryFilter> GetMergedDiscoveryFilterMasked(
+      BluetoothDiscoveryFilter* masked_filter) const;
 
   // Requests the list of devices from the adapter. All devices are returned,
   // including those currently connected and those paired. Use the returned
@@ -395,12 +409,22 @@
   //    - If the count is greater than 1, decrement the count and return
   //      success.
   //
+  // |discovery_filter| passed to AddDiscoverySession and RemoveDiscoverySession
+  // is owned by other objects and shall not be freed.
+  //
   // These methods invoke |callback| for success and |error_callback| for
   // failures.
-  virtual void AddDiscoverySession(const base::Closure& callback,
+  virtual void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                                   const base::Closure& callback,
                                    const ErrorCallback& error_callback) = 0;
-  virtual void RemoveDiscoverySession(const base::Closure& callback,
-                                      const ErrorCallback& error_callback) = 0;
+  virtual void RemoveDiscoverySession(
+      BluetoothDiscoveryFilter* discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) = 0;
+  virtual void SetDiscoveryFilter(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) = 0;
 
   // Called by RemovePairingDelegate() in order to perform any class-specific
   // internal functionality necessary to remove the pairing delegate, such as
@@ -409,7 +433,9 @@
       BluetoothDevice::PairingDelegate* pairing_delegate) = 0;
 
   // Success callback passed to AddDiscoverySession by StartDiscoverySession.
-  void OnStartDiscoverySession(const DiscoverySessionCallback& callback);
+  void OnStartDiscoverySession(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+      const DiscoverySessionCallback& callback);
 
   // Marks all known DiscoverySession instances as inactive. Called by
   // BluetoothAdapter in the event that the adapter unexpectedly stops
@@ -435,6 +461,12 @@
   std::list<PairingDelegatePair> pairing_delegates_;
 
  private:
+  // Return all discovery filters assigned to this adapter merged together.
+  // If |omit| is true, |discovery_filter| will not be processed.
+  scoped_ptr<BluetoothDiscoveryFilter> GetMergedDiscoveryFilterHelper(
+      const BluetoothDiscoveryFilter* discovery_filter,
+      bool omit) const;
+
   // List of active DiscoverySession objects. This is used to notify sessions to
   // become inactive in case of an unexpected change to the adapter discovery
   // state. We keep raw pointers, with the invariant that a DiscoverySession
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc
index edd0f01..fce8f8b 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.cc
+++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -36,6 +36,7 @@
 using device::BluetoothAdapter;
 using device::BluetoothAudioSink;
 using device::BluetoothDevice;
+using device::BluetoothDiscoveryFilter;
 using device::BluetoothSocket;
 using device::BluetoothUUID;
 
@@ -1091,6 +1092,7 @@
 }
 
 void BluetoothAdapterChromeOS::AddDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   if (!IsPresent()) {
@@ -1131,6 +1133,7 @@
 }
 
 void BluetoothAdapterChromeOS::RemoveDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   if (!IsPresent()) {
@@ -1181,6 +1184,13 @@
                      error_callback));
 }
 
+void BluetoothAdapterChromeOS::SetDiscoveryFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  // TODO(jpawlowski): Implement
+}
+
 void BluetoothAdapterChromeOS::OnStartDiscovery(
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
@@ -1263,7 +1273,7 @@
     VLOG(1) << "Process queued discovery request.";
     DiscoveryCallbackPair callbacks = discovery_request_queue_.front();
     discovery_request_queue_.pop();
-    AddDiscoverySession(callbacks.first, callbacks.second);
+    AddDiscoverySession(nullptr, callbacks.first, callbacks.second);
 
     // If the queued request resulted in a pending call, then let it
     // asynchonously process the remaining queued requests once the pending
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h
index d8c1d9b..39a23dec2 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -23,6 +23,7 @@
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_audio_sink.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
 #include "device/bluetooth/bluetooth_export.h"
 
 namespace device {
@@ -263,10 +264,17 @@
                                  bool success);
 
   // BluetoothAdapter:
-  void AddDiscoverySession(const base::Closure& callback,
+  void AddDiscoverySession(device::BluetoothDiscoveryFilter* discovery_filter,
+                           const base::Closure& callback,
                            const ErrorCallback& error_callback) override;
-  void RemoveDiscoverySession(const base::Closure& callback,
-                              const ErrorCallback& error_callback) override;
+  void RemoveDiscoverySession(
+      device::BluetoothDiscoveryFilter* discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) override;
+  void SetDiscoveryFilter(
+      scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) override;
 
   // Called by dbus:: on completion of the D-Bus method call to start discovery.
   void OnStartDiscovery(const base::Closure& callback,
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h
index 52567b04..bca4389 100644
--- a/device/bluetooth/bluetooth_adapter_mac.h
+++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -95,10 +95,15 @@
 
   // BluetoothAdapter:
   void DeleteOnCorrectThread() const override;
-  void AddDiscoverySession(const base::Closure& callback,
+  void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                           const base::Closure& callback,
                            const ErrorCallback& error_callback) override;
-  void RemoveDiscoverySession(const base::Closure& callback,
+  void RemoveDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                              const base::Closure& callback,
                               const ErrorCallback& error_callback) override;
+  void SetDiscoveryFilter(scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+                          const base::Closure& callback,
+                          const ErrorCallback& error_callback) override;
 
   void Init();
   void InitForTest(scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index fc75414..7c04a433 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -180,6 +180,7 @@
 }
 
 void BluetoothAdapterMac::AddDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   DVLOG(1) << __func__;
@@ -207,6 +208,7 @@
 }
 
 void BluetoothAdapterMac::RemoveDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   DVLOG(1) << __func__;
@@ -236,6 +238,14 @@
   callback.Run();
 }
 
+void BluetoothAdapterMac::SetDiscoveryFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+  error_callback.Run();
+}
+
 void BluetoothAdapterMac::RemovePairingDelegateInternal(
     BluetoothDevice::PairingDelegate* pairing_delegate) {
 }
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
index 5dd10b1..81e2dcb 100644
--- a/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/bind.h"
 #include "base/memory/ref_counted.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using device::BluetoothAdapter;
@@ -49,9 +51,15 @@
 
   void DeleteOnCorrectThread() const override { delete this; }
 
+  void StartDiscoverySessionWithFilter(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+      const DiscoverySessionCallback& callback,
+      const ErrorCallback& error_callback) override {
+    OnStartDiscoverySession(discovery_filter.Pass(), callback);
+  }
+
   void StartDiscoverySession(const DiscoverySessionCallback& callback,
                              const ErrorCallback& error_callback) override {}
-
   void CreateRfcommService(
       const BluetoothUUID& uuid,
       const ServiceOptions& options,
@@ -69,15 +77,42 @@
       const AcquiredCallback& callback,
       const BluetoothAudioSink::ErrorCallback& error_callback) override {}
 
+  void TestErrorCallback() {}
+
+  ScopedVector<BluetoothDiscoverySession> discovery_sessions_;
+
+  void TestOnStartDiscoverySession(
+      scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
+    discovery_sessions_.push_back(discovery_session.Pass());
+  }
+
+  void CleanupSessions() { discovery_sessions_.clear(); }
+
+  void InjectFilteredSession(
+      scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter) {
+    StartDiscoverySessionWithFilter(
+        discovery_filter.Pass(),
+        base::Bind(&TestBluetoothAdapter::TestOnStartDiscoverySession,
+                   base::Unretained(this)),
+        base::Bind(&TestBluetoothAdapter::TestErrorCallback,
+                   base::Unretained(this)));
+  }
+
  protected:
   ~TestBluetoothAdapter() override {}
 
-  void AddDiscoverySession(const base::Closure& callback,
+  void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                           const base::Closure& callback,
                            const ErrorCallback& error_callback) override {}
 
-  void RemoveDiscoverySession(const base::Closure& callback,
+  void RemoveDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                              const base::Closure& callback,
                               const ErrorCallback& error_callback) override {}
 
+  void SetDiscoveryFilter(scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+                          const base::Closure& callback,
+                          const ErrorCallback& error_callback) override {}
+
   void RemovePairingDelegateInternal(
       BluetoothDevice::PairingDelegate* pairing_delegate) override {}
 };
@@ -162,4 +197,202 @@
   EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL);
 }
 
+TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterEmpty) {
+  scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter();
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter;
+
+  discovery_filter = adapter->GetMergedDiscoveryFilter();
+  EXPECT_TRUE(discovery_filter.get() == nullptr);
+
+  discovery_filter = adapter->GetMergedDiscoveryFilterMasked(nullptr);
+  EXPECT_TRUE(discovery_filter.get() == nullptr);
+}
+
+TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterRegular) {
+  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter;
+
+  // make sure adapter have one session wihout filtering.
+  adapter->InjectFilteredSession(discovery_filter.Pass());
+
+  // having one reglar session should result in no filter
+  scoped_ptr<BluetoothDiscoveryFilter> resulting_filter =
+      adapter->GetMergedDiscoveryFilter();
+  EXPECT_TRUE(resulting_filter.get() == nullptr);
+
+  // omiting no filter when having one reglar session should result in no filter
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(nullptr);
+  EXPECT_TRUE(resulting_filter.get() == nullptr);
+
+  adapter->CleanupSessions();
+}
+
+TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterRssi) {
+  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
+  int16_t resulting_rssi;
+  uint16_t resulting_pathloss;
+  scoped_ptr<BluetoothDiscoveryFilter> resulting_filter;
+
+  BluetoothDiscoveryFilter* df = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df->SetRSSI(-30);
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(df);
+
+  BluetoothDiscoveryFilter* df2 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df2->SetRSSI(-65);
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);
+
+  // make sure adapter have one session wihout filtering.
+  adapter->InjectFilteredSession(discovery_filter.Pass());
+
+  // DO_NOTHING should have no impact
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  resulting_filter->GetRSSI(&resulting_rssi);
+  EXPECT_EQ(-30, resulting_rssi);
+
+  // should not use df2 at all, as it's not associated with adapter yet
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
+  resulting_filter->GetRSSI(&resulting_rssi);
+  EXPECT_EQ(-30, resulting_rssi);
+
+  adapter->InjectFilteredSession(discovery_filter2.Pass());
+
+  // result of merging two rssi values should be lower one
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  resulting_filter->GetRSSI(&resulting_rssi);
+  EXPECT_EQ(-65, resulting_rssi);
+
+  // ommit bigger value, result should stay same
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
+  resulting_filter->GetRSSI(&resulting_rssi);
+  EXPECT_EQ(-65, resulting_rssi);
+
+  // ommit lower value, result should change
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
+  resulting_filter->GetRSSI(&resulting_rssi);
+  EXPECT_EQ(-30, resulting_rssi);
+
+  BluetoothDiscoveryFilter* df3 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df3->SetPathloss(60);
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);
+
+  // when rssi and pathloss are merged, both should be cleared, becuase there is
+  // no way to tell which filter will be more generic
+  adapter->InjectFilteredSession(discovery_filter3.Pass());
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  EXPECT_FALSE(resulting_filter->GetRSSI(&resulting_rssi));
+  EXPECT_FALSE(resulting_filter->GetPathloss(&resulting_pathloss));
+
+  adapter->CleanupSessions();
+}
+
+TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterTransport) {
+  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
+  scoped_ptr<BluetoothDiscoveryFilter> resulting_filter;
+
+  BluetoothDiscoveryFilter* df = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(df);
+
+  BluetoothDiscoveryFilter* df2 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);
+
+  adapter->InjectFilteredSession(discovery_filter.Pass());
+
+  // Just one filter, make sure transport was properly rewritten
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC,
+            resulting_filter->GetTransport());
+
+  adapter->InjectFilteredSession(discovery_filter2.Pass());
+
+  // Two filters, should have OR of both transport's
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL,
+            resulting_filter->GetTransport());
+
+  // When 1st filter is masked, 2nd filter transport should be returned.
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_LE,
+            resulting_filter->GetTransport());
+
+  // When 2nd filter is masked, 1st filter transport should be returned.
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df2);
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC,
+            resulting_filter->GetTransport());
+
+  BluetoothDiscoveryFilter* df3 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df3->CopyFrom(BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL));
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);
+
+  // Merging empty filter in should result in empty filter
+  adapter->InjectFilteredSession(discovery_filter3.Pass());
+  resulting_filter = adapter->GetMergedDiscoveryFilter();
+  EXPECT_TRUE(resulting_filter->IsDefault());
+
+  adapter->CleanupSessions();
+}
+
+TEST(BluetoothAdapterTest, GetMergedDiscoveryFilterAllFields) {
+  scoped_refptr<TestBluetoothAdapter> adapter = new TestBluetoothAdapter();
+  int16_t resulting_rssi;
+  std::set<device::BluetoothUUID> resulting_uuids;
+
+  BluetoothDiscoveryFilter* df = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df->SetRSSI(-60);
+  df->AddUUID(device::BluetoothUUID("1000"));
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(df);
+
+  BluetoothDiscoveryFilter* df2 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df2->SetRSSI(-85);
+  df2->SetTransport(BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df2->AddUUID(device::BluetoothUUID("1020"));
+  df2->AddUUID(device::BluetoothUUID("1001"));
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter2(df2);
+
+  BluetoothDiscoveryFilter* df3 = new BluetoothDiscoveryFilter(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df3->SetRSSI(-65);
+  df3->SetTransport(BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  df3->AddUUID(device::BluetoothUUID("1020"));
+  df3->AddUUID(device::BluetoothUUID("1003"));
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter3(df3);
+
+  // make sure adapter have one session wihout filtering.
+  adapter->InjectFilteredSession(discovery_filter.Pass());
+  adapter->InjectFilteredSession(discovery_filter2.Pass());
+  adapter->InjectFilteredSession(discovery_filter3.Pass());
+
+  scoped_ptr<BluetoothDiscoveryFilter> resulting_filter =
+      adapter->GetMergedDiscoveryFilter();
+  resulting_filter->GetRSSI(&resulting_rssi);
+  resulting_filter->GetUUIDs(resulting_uuids);
+  EXPECT_TRUE(resulting_filter->GetTransport());
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL,
+            resulting_filter->GetTransport());
+  EXPECT_EQ(-85, resulting_rssi);
+  EXPECT_EQ(4UL, resulting_uuids.size());
+  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1000")) !=
+              resulting_uuids.end());
+  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1001")) !=
+              resulting_uuids.end());
+  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1003")) !=
+              resulting_uuids.end());
+  EXPECT_TRUE(resulting_uuids.find(device::BluetoothUUID("1020")) !=
+              resulting_uuids.end());
+
+  resulting_filter = adapter->GetMergedDiscoveryFilterMasked(df);
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL,
+            resulting_filter->GetTransport());
+
+  adapter->CleanupSessions();
+}
+
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc
index fb28d7cd..a9fc20034 100644
--- a/device/bluetooth/bluetooth_adapter_win.cc
+++ b/device/bluetooth/bluetooth_adapter_win.cc
@@ -300,6 +300,7 @@
 // If the method is called when |discovery_status_| is DISCOVERY_STOPPING,
 // starting again is handled by BluetoothAdapterWin::DiscoveryStopped().
 void BluetoothAdapterWin::AddDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   if (discovery_status_ == DISCOVERING) {
@@ -313,6 +314,7 @@
 }
 
 void BluetoothAdapterWin::RemoveDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
   if (discovery_status_ == NOT_DISCOVERING) {
@@ -323,6 +325,14 @@
   MaybePostStopDiscoveryTask();
 }
 
+void BluetoothAdapterWin::SetDiscoveryFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+  error_callback.Run();
+}
+
 void BluetoothAdapterWin::Init() {
   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
   socket_thread_ = BluetoothSocketThread::Get();
diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h
index 72280ee8..2b086a2f 100644
--- a/device/bluetooth/bluetooth_adapter_win.h
+++ b/device/bluetooth/bluetooth_adapter_win.h
@@ -18,6 +18,7 @@
 #include "base/threading/thread_checker.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_audio_sink.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/bluetooth_task_manager_win.h"
 
@@ -112,9 +113,15 @@
   // BluetoothAdapter:
   void DeleteOnCorrectThread() const override;
   virtual void AddDiscoverySession(
+      BluetoothDiscoveryFilter* discovery_filter,
       const base::Closure& callback,
       const ErrorCallback& error_callback) override;
   virtual void RemoveDiscoverySession(
+      BluetoothDiscoveryFilter* discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) override;
+  virtual void SetDiscoveryFilter(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
       const base::Closure& callback,
       const ErrorCallback& error_callback) override;
 
diff --git a/device/bluetooth/bluetooth_adapter_win_unittest.cc b/device/bluetooth/bluetooth_adapter_win_unittest.cc
index 4a7abd7..670a03dc2 100644
--- a/device/bluetooth/bluetooth_adapter_win_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_win_unittest.cc
@@ -154,13 +154,13 @@
   void CallAddDiscoverySession(
       const base::Closure& callback,
       const BluetoothAdapter::ErrorCallback& error_callback) {
-    adapter_win_->AddDiscoverySession(callback, error_callback);
+    adapter_win_->AddDiscoverySession(nullptr, callback, error_callback);
   }
 
   void CallRemoveDiscoverySession(
       const base::Closure& callback,
       const BluetoothAdapter::ErrorCallback& error_callback) {
-    adapter_win_->RemoveDiscoverySession(callback, error_callback);
+    adapter_win_->RemoveDiscoverySession(nullptr, callback, error_callback);
   }
 
  protected:
diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc
index da03501..535e047 100644
--- a/device/bluetooth/bluetooth_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_chromeos_unittest.cc
@@ -3251,11 +3251,13 @@
   EXPECT_EQ(0, callback_count_) << "OnPropertyChangeCompleted error";
   EXPECT_EQ(1, error_callback_count_--) << "OnPropertyChangeCompleted error";
 
-  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                         GetErrorCallback());
   EXPECT_EQ(0, callback_count_) << "AddDiscoverySession error";
   EXPECT_EQ(1, error_callback_count_--) << "AddDiscoverySession error";
 
-  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->RemoveDiscoverySession(nullptr, GetCallback(),
+                                            GetErrorCallback());
   EXPECT_EQ(0, callback_count_) << "RemoveDiscoverySession error";
   EXPECT_EQ(1, error_callback_count_--) << "RemoveDiscoverySession error";
 
@@ -3321,7 +3323,8 @@
       static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
 
   for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
-    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+    adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                           GetErrorCallback());
   }
   adapter_->Shutdown();
   adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
@@ -3338,7 +3341,8 @@
       static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
 
   for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
-    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+    adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                           GetErrorCallback());
   }
   adapter_->Shutdown();
   adapter_chrome_os->OnStartDiscoveryError(GetCallback(), GetErrorCallback(),
@@ -3357,14 +3361,17 @@
 
   // In order to queue up discovery sessions before an OnStopDiscovery call
   // RemoveDiscoverySession must be called, so Add, Start, and Remove:
-  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                         GetErrorCallback());
   adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
-  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->RemoveDiscoverySession(nullptr, GetCallback(),
+                                            GetErrorCallback());
   callback_count_ = 0;
   error_callback_count_ = 0;
   // Can now queue discovery sessions while waiting for OnStopDiscovery.
   for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
-    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+    adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                           GetErrorCallback());
   }
   adapter_->Shutdown();
   adapter_chrome_os->OnStopDiscovery(GetCallback());
@@ -3384,14 +3391,17 @@
 
   // In order to queue up discovery sessions before an OnStopDiscoveryError call
   // RemoveDiscoverySession must be called, so Add, Start, and Remove:
-  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                         GetErrorCallback());
   adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
-  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->RemoveDiscoverySession(nullptr, GetCallback(),
+                                            GetErrorCallback());
   callback_count_ = 0;
   error_callback_count_ = 0;
   // Can now queue discovery sessions while waiting for OnStopDiscoveryError.
   for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
-    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+    adapter_chrome_os->AddDiscoverySession(nullptr, GetCallback(),
+                                           GetErrorCallback());
   }
   adapter_->Shutdown();
   adapter_chrome_os->OnStopDiscoveryError(GetErrorCallback(), "", "");
diff --git a/device/bluetooth/bluetooth_discovery_filter_unittest.cc b/device/bluetooth/bluetooth_discovery_filter_unittest.cc
new file mode 100644
index 0000000..ea7a04f
--- /dev/null
+++ b/device/bluetooth/bluetooth_discovery_filter_unittest.cc
@@ -0,0 +1,181 @@
+// 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 "device/bluetooth/bluetooth_discovery_session.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const device::BluetoothUUID uuid1003("1003");
+const device::BluetoothUUID uuid1004("1004");
+const device::BluetoothUUID uuid1020("1020");
+
+}  // namespace
+
+namespace device {
+
+TEST(BluetoothDiscoveryFilterTest, Equal) {
+  BluetoothDiscoveryFilter df1(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  df1.SetRSSI(-65);
+  df1.AddUUID(uuid1020);
+  df1.AddUUID(uuid1003);
+
+  BluetoothDiscoveryFilter df2(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  df2.SetRSSI(-65);
+  df2.AddUUID(uuid1020);
+  df2.AddUUID(uuid1004);
+
+  // uuids are not same, so should fail
+  ASSERT_FALSE(df1.Equals(df2));
+
+  // make filters equal
+  df1.AddUUID(uuid1004);
+  df2.AddUUID(uuid1003);
+  ASSERT_TRUE(df1.Equals(df2));
+
+  // now transport don't match
+  df1.SetTransport(BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  ASSERT_FALSE(df1.Equals(df2));
+
+  // now everything is back matching
+  df1.SetTransport(BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  ASSERT_TRUE(df1.Equals(df2));
+
+  // now rssi don't match
+  df1.SetRSSI(-30);
+  ASSERT_FALSE(df1.Equals(df2));
+
+  BluetoothDiscoveryFilter df3(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  df3.SetPathloss(45);
+  df3.AddUUID(uuid1020);
+  df3.AddUUID(uuid1003);
+  df3.AddUUID(uuid1004);
+
+  // Having Pathloss and RSSI set in two different filter makes them unequal.
+  ASSERT_FALSE(df1.Equals(df3));
+}
+
+TEST(BluetoothDiscoveryFilterTest, CopyFrom) {
+  BluetoothDiscoveryFilter df1(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+  df1.SetRSSI(-65);
+  df1.AddUUID(uuid1020);
+  df1.AddUUID(uuid1003);
+
+  BluetoothDiscoveryFilter df2(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+
+  df2.CopyFrom(df1);
+
+  int16_t out_rssi;
+  std::set<device::BluetoothUUID> out_uuids;
+
+  // make sure all properties were copied
+  df2.GetRSSI(&out_rssi);
+  EXPECT_EQ(-65, out_rssi);
+
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC,
+            df2.GetTransport());
+
+  df2.GetUUIDs(out_uuids);
+  EXPECT_TRUE(out_uuids.find(uuid1020) != out_uuids.end());
+  EXPECT_TRUE(out_uuids.find(uuid1003) != out_uuids.end());
+}
+
+TEST(BluetoothDiscoveryFilterTest, MergeUUIDs) {
+  BluetoothDiscoveryFilter df1(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df1.AddUUID(uuid1020);
+  df1.AddUUID(uuid1003);
+
+  BluetoothDiscoveryFilter df2(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df2.AddUUID(uuid1020);
+  df2.AddUUID(uuid1004);
+
+  scoped_ptr<BluetoothDiscoveryFilter> df3 =
+      BluetoothDiscoveryFilter::Merge(&df1, &df2);
+
+  // df3 should contain all uuids from df1 and df2
+  std::set<device::BluetoothUUID> out_uuids;
+  df3->GetUUIDs(out_uuids);
+  EXPECT_TRUE(out_uuids.find(uuid1020) != out_uuids.end());
+  EXPECT_TRUE(out_uuids.find(uuid1003) != out_uuids.end());
+  EXPECT_TRUE(out_uuids.find(uuid1004) != out_uuids.end());
+
+  // Merging with empty filter would return empty filter
+  df3 = BluetoothDiscoveryFilter::Merge(&df1, nullptr);
+  df3->GetUUIDs(out_uuids);
+  EXPECT_EQ(0UL, out_uuids.size());
+  EXPECT_TRUE(df3->IsDefault());
+}
+
+TEST(BluetoothDiscoveryFilterTest, MergeProximity) {
+  BluetoothDiscoveryFilter df1(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df1.SetRSSI(-50);
+
+  BluetoothDiscoveryFilter df2(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df2.SetRSSI(-70);
+
+  BluetoothDiscoveryFilter df3(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df3.SetPathloss(70);
+
+  BluetoothDiscoveryFilter df4(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+  df4.SetPathloss(20);
+
+  scoped_ptr<BluetoothDiscoveryFilter> result =
+      BluetoothDiscoveryFilter::Merge(&df1, &df2);
+
+  int16_t out_rssi;
+  // Merging RSSI should return smaller of both values
+  EXPECT_TRUE(result->GetRSSI(&out_rssi));
+  EXPECT_EQ(-70, out_rssi);
+
+  uint16_t out_pathloss;
+  // Merging RSSI with Pathloss should clear proximity
+  result = BluetoothDiscoveryFilter::Merge(&df1, &df3);
+  EXPECT_FALSE(result->GetRSSI(&out_rssi));
+  EXPECT_FALSE(result->GetPathloss(&out_pathloss));
+
+  // Merging Pathloss should return bigger of both values
+  result = BluetoothDiscoveryFilter::Merge(&df3, &df4);
+  EXPECT_TRUE(result->GetPathloss(&out_pathloss));
+  EXPECT_EQ(70, out_pathloss);
+}
+
+TEST(BluetoothDiscoveryFilterTest, MergeTransport) {
+  BluetoothDiscoveryFilter df1(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC);
+
+  BluetoothDiscoveryFilter df2(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_LE);
+
+  BluetoothDiscoveryFilter df3(
+      BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL);
+
+  scoped_ptr<BluetoothDiscoveryFilter> result =
+      BluetoothDiscoveryFilter::Merge(&df1, &df2);
+
+  // Merging LE and CLASSIC should result in both being set
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL,
+            result->GetTransport());
+
+  result = BluetoothDiscoveryFilter::Merge(&df1, &df3);
+  EXPECT_EQ(BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL,
+            result->GetTransport());
+
+  // Merging with null should alway result with empty filter.
+  result = BluetoothDiscoveryFilter::Merge(&df1, nullptr);
+  EXPECT_TRUE(result->IsDefault());
+}
+
+}  // namespace device
diff --git a/device/bluetooth/bluetooth_discovery_session.cc b/device/bluetooth/bluetooth_discovery_session.cc
index bfcc59f..e30f86cc 100644
--- a/device/bluetooth/bluetooth_discovery_session.cc
+++ b/device/bluetooth/bluetooth_discovery_session.cc
@@ -9,9 +9,180 @@
 
 namespace device {
 
+BluetoothDiscoveryFilter::BluetoothDiscoveryFilter(TransportMask transport) {
+  SetTransport(transport);
+}
+
+BluetoothDiscoveryFilter::~BluetoothDiscoveryFilter() {
+}
+
+bool BluetoothDiscoveryFilter::GetRSSI(int16_t* out_rssi) const {
+  DCHECK(out_rssi);
+  if (!rssi_.get())
+    return false;
+
+  *out_rssi = *rssi_;
+  return true;
+}
+
+void BluetoothDiscoveryFilter::SetRSSI(int16_t rssi) {
+  if (!rssi_.get())
+    rssi_.reset(new int16_t());
+
+  *rssi_ = rssi;
+}
+
+bool BluetoothDiscoveryFilter::GetPathloss(uint16_t* out_pathloss) const {
+  DCHECK(out_pathloss);
+  if (!pathloss_.get())
+    return false;
+
+  *out_pathloss = *pathloss_;
+  return true;
+}
+
+void BluetoothDiscoveryFilter::SetPathloss(uint16_t pathloss) {
+  if (!pathloss_.get())
+    pathloss_.reset(new uint16_t());
+
+  *pathloss_ = pathloss;
+}
+
+BluetoothDiscoveryFilter::TransportMask BluetoothDiscoveryFilter::GetTransport()
+    const {
+  return transport_;
+}
+
+void BluetoothDiscoveryFilter::SetTransport(TransportMask transport) {
+  DCHECK(transport > 0 && transport < 4);
+  transport_ = transport;
+}
+
+void BluetoothDiscoveryFilter::GetUUIDs(
+    std::set<device::BluetoothUUID>& out_uuids) const {
+  out_uuids.clear();
+
+  for (auto& uuid : uuids_)
+    out_uuids.insert(*uuid);
+}
+
+void BluetoothDiscoveryFilter::AddUUID(const device::BluetoothUUID& uuid) {
+  DCHECK(uuid.IsValid());
+  for (auto& uuid_it : uuids_) {
+    if (*uuid_it == uuid)
+      return;
+  }
+
+  uuids_.push_back(new device::BluetoothUUID(uuid));
+}
+
+void BluetoothDiscoveryFilter::CopyFrom(
+    const BluetoothDiscoveryFilter& filter) {
+  transport_ = filter.transport_;
+
+  if (filter.uuids_.size()) {
+    for (auto& uuid : filter.uuids_)
+      AddUUID(*uuid);
+  } else
+    uuids_.clear();
+
+  if (filter.rssi_.get()) {
+    SetRSSI(*filter.rssi_);
+  } else
+    rssi_.reset();
+
+  if (filter.pathloss_.get()) {
+    SetPathloss(*filter.pathloss_);
+  } else
+    pathloss_.reset();
+}
+
+scoped_ptr<device::BluetoothDiscoveryFilter> BluetoothDiscoveryFilter::Merge(
+    const device::BluetoothDiscoveryFilter* filter_a,
+    const device::BluetoothDiscoveryFilter* filter_b) {
+  scoped_ptr<BluetoothDiscoveryFilter> result;
+
+  if (!filter_a && !filter_b) {
+    return result;
+  }
+
+  result.reset(new BluetoothDiscoveryFilter(Transport::TRANSPORT_DUAL));
+
+  if (!filter_a || !filter_b || filter_a->IsDefault() ||
+      filter_b->IsDefault()) {
+    return result;
+  }
+
+  // both filters are not empty, so they must have transport set.
+  result->SetTransport(filter_a->transport_ | filter_b->transport_);
+
+  // if both filters have uuids, them merge them. Otherwise uuids filter should
+  // be left empty
+  if (filter_a->uuids_.size() && filter_b->uuids_.size()) {
+    std::set<device::BluetoothUUID> uuids;
+    filter_a->GetUUIDs(uuids);
+    for (auto& uuid : uuids)
+      result->AddUUID(uuid);
+
+    filter_b->GetUUIDs(uuids);
+    for (auto& uuid : uuids)
+      result->AddUUID(uuid);
+  }
+
+  if ((filter_a->rssi_.get() && filter_b->pathloss_.get()) ||
+      (filter_a->pathloss_.get() && filter_b->rssi_.get())) {
+    // if both rssi and pathloss filtering is enabled in two different
+    // filters, we can't tell which filter is more generic, and we don't set
+    // proximity filtering on merged filter.
+    return result;
+  }
+
+  if (filter_a->rssi_.get() && filter_b->rssi_.get()) {
+    result->SetRSSI(std::min(*filter_a->rssi_, *filter_b->rssi_));
+  } else if (filter_a->pathloss_.get() && filter_b->pathloss_.get()) {
+    result->SetPathloss(std::max(*filter_a->pathloss_, *filter_b->pathloss_));
+  }
+
+  return result;
+}
+
+bool BluetoothDiscoveryFilter::Equals(
+    const BluetoothDiscoveryFilter& other) const {
+  if (((!!rssi_.get()) != (!!other.rssi_.get())) ||
+      (rssi_.get() && other.rssi_.get() && *rssi_ != *other.rssi_)) {
+    return false;
+  }
+
+  if (((!!pathloss_.get()) != (!!other.pathloss_.get())) ||
+      (pathloss_.get() && other.pathloss_.get() &&
+       *pathloss_ != *other.pathloss_)) {
+    return false;
+  }
+
+  if (transport_ != other.transport_)
+    return false;
+
+  std::set<device::BluetoothUUID> uuids_a, uuids_b;
+  GetUUIDs(uuids_a);
+  other.GetUUIDs(uuids_b);
+  if (uuids_a != uuids_b)
+    return false;
+
+  return true;
+}
+
+bool BluetoothDiscoveryFilter::IsDefault() const {
+  return !(rssi_.get() || pathloss_.get() || uuids_.size() ||
+           transport_ != Transport::TRANSPORT_DUAL);
+}
+
 BluetoothDiscoverySession::BluetoothDiscoverySession(
-    scoped_refptr<BluetoothAdapter> adapter)
-    : active_(true), adapter_(adapter), weak_ptr_factory_(this) {
+    scoped_refptr<BluetoothAdapter> adapter,
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter)
+    : active_(true),
+      adapter_(adapter),
+      discovery_filter_(discovery_filter.release()),
+      weak_ptr_factory_(this) {
   DCHECK(adapter_.get());
 }
 
@@ -36,14 +207,15 @@
   }
   VLOG(1) << "Stopping device discovery session.";
   adapter_->RemoveDiscoverySession(
+      discovery_filter_.get(),
       base::Bind(&BluetoothDiscoverySession::OnStop,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 callback),
+                 weak_ptr_factory_.GetWeakPtr(), callback),
       error_callback);
 }
 
 void BluetoothDiscoverySession::OnStop(const base::Closure& callback) {
   MarkAsInactive();
+  discovery_filter_.reset();
   callback.Run();
 }
 
@@ -54,4 +226,18 @@
   adapter_->DiscoverySessionBecameInactive(this);
 }
 
+void BluetoothDiscoverySession::SetDiscoveryFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  discovery_filter_.reset(discovery_filter.release());
+  adapter_->SetDiscoveryFilter(adapter_->GetMergedDiscoveryFilter().Pass(),
+                               callback, error_callback);
+}
+
+const BluetoothDiscoveryFilter* BluetoothDiscoverySession::GetDiscoveryFilter()
+    const {
+  return discovery_filter_.get();
+}
+
 }  // namespace device
diff --git a/device/bluetooth/bluetooth_discovery_session.h b/device/bluetooth/bluetooth_discovery_session.h
index 2dae0de..d50bf9cd 100644
--- a/device/bluetooth/bluetooth_discovery_session.h
+++ b/device/bluetooth/bluetooth_discovery_session.h
@@ -8,11 +8,67 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_export.h"
 
 namespace device {
 
-class BluetoothAdapter;
+// used to keep discovery filter that might be Used to limit reported devices.
+class DEVICE_BLUETOOTH_EXPORT BluetoothDiscoveryFilter {
+ public:
+  // Possible transports to use for scan filter.
+  enum Transport {
+    TRANSPORT_CLASSIC = 0x01,
+    TRANSPORT_LE = 0x02,
+    TRANSPORT_DUAL = (TRANSPORT_CLASSIC | TRANSPORT_LE)
+  };
+  using TransportMask = uint8_t;
+
+  BluetoothDiscoveryFilter(TransportMask transport);
+  ~BluetoothDiscoveryFilter();
+
+  // These getters return true when given field is set in filter, and copy this
+  // value to |out_*| parameter. If value is not set, returns false.
+  // Thes setters assign given value to proper filter field.
+  bool GetRSSI(int16_t* out_rssi) const;
+  void SetRSSI(int16_t rssi);
+  bool GetPathloss(uint16_t* out_pathloss) const;
+  void SetPathloss(uint16_t pathloss);
+
+  // Return and set transport field of this filter.
+  TransportMask GetTransport() const;
+  void SetTransport(TransportMask transport);
+
+  // Make |out_uuids| represent all uuids assigned to this filter.
+  void GetUUIDs(std::set<device::BluetoothUUID>& out_uuids) const;
+
+  // Add UUID to internal UUIDs filter. If UUIDs filter doesn't exist, it will
+  // be created.
+  void AddUUID(const device::BluetoothUUID& uuid);
+
+  // Copy content of |filter| and assigns it to this filter.
+  void CopyFrom(const BluetoothDiscoveryFilter& filter);
+
+  // Check if two filters are equal.
+  bool Equals(const BluetoothDiscoveryFilter& filter) const;
+
+  // Returns true if all fields in filter are empty
+  bool IsDefault() const;
+
+  // Returns result of merging two filters together. If at least one of the
+  // filters is NULL this will return an empty filter
+  static scoped_ptr<device::BluetoothDiscoveryFilter> Merge(
+      const device::BluetoothDiscoveryFilter* filter_a,
+      const device::BluetoothDiscoveryFilter* filter_b);
+
+ private:
+  scoped_ptr<int16_t> rssi_;
+  scoped_ptr<uint16_t> pathloss_;
+  TransportMask transport_;
+  ScopedVector<device::BluetoothUUID> uuids_;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryFilter);
+};
 
 // BluetoothDiscoverySession represents a current active or inactive device
 // discovery session. Instances of this class are obtained by calling
@@ -58,8 +114,17 @@
   virtual void Stop(const base::Closure& callback,
                     const ErrorCallback& error_callback);
 
+  virtual void SetDiscoveryFilter(
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback);
+
+  virtual const BluetoothDiscoveryFilter* GetDiscoveryFilter() const;
+
  protected:
-  explicit BluetoothDiscoverySession(scoped_refptr<BluetoothAdapter> adapter);
+  explicit BluetoothDiscoverySession(
+      scoped_refptr<BluetoothAdapter> adapter,
+      scoped_ptr<BluetoothDiscoveryFilter> discovery_filter);
 
  private:
   friend class BluetoothAdapter;
@@ -78,6 +143,9 @@
   // The adapter that created this instance.
   scoped_refptr<BluetoothAdapter> adapter_;
 
+  // Filter assigned to this session, if any
+  scoped_ptr<BluetoothDiscoveryFilter> discovery_filter_;
+
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
   base::WeakPtrFactory<BluetoothDiscoverySession> weak_ptr_factory_;
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.cc b/device/bluetooth/test/mock_bluetooth_adapter.cc
index 2f797d3..1ee00ee0 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.cc
+++ b/device/bluetooth/test/mock_bluetooth_adapter.cc
@@ -24,11 +24,21 @@
 };
 
 void MockBluetoothAdapter::AddDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
-    const ErrorCallback& error_callback) {}
+    const ErrorCallback& error_callback) {
+}
 
 void MockBluetoothAdapter::RemoveDiscoverySession(
+    BluetoothDiscoveryFilter* discovery_filter,
     const base::Closure& callback,
-    const ErrorCallback& error_callback) {}
+    const ErrorCallback& error_callback) {
+}
+
+void MockBluetoothAdapter::SetDiscoveryFilter(
+    scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+}
 
 }  // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h
index fd67936..d338256 100644
--- a/device/bluetooth/test/mock_bluetooth_adapter.h
+++ b/device/bluetooth/test/mock_bluetooth_adapter.h
@@ -11,6 +11,7 @@
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_audio_sink.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_discovery_session.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace device {
@@ -87,10 +88,15 @@
 
  protected:
   void DeleteOnCorrectThread() const override;
-  virtual void AddDiscoverySession(const base::Closure& callback,
-                                   const ErrorCallback& error_callback);
-  virtual void RemoveDiscoverySession(const base::Closure& callback,
-                                      const ErrorCallback& error_callback);
+  void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                           const base::Closure& callback,
+                           const ErrorCallback& error_callback) override;
+  void RemoveDiscoverySession(BluetoothDiscoveryFilter* discovery_filter,
+                              const base::Closure& callback,
+                              const ErrorCallback& error_callback) override;
+  void SetDiscoveryFilter(scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+                          const base::Closure& callback,
+                          const ErrorCallback& error_callback) override;
   virtual ~MockBluetoothAdapter();
 
   MOCK_METHOD1(RemovePairingDelegateInternal,
diff --git a/device/bluetooth/test/mock_bluetooth_discovery_session.cc b/device/bluetooth/test/mock_bluetooth_discovery_session.cc
index 295f9546..1aea85a 100644
--- a/device/bluetooth/test/mock_bluetooth_discovery_session.cc
+++ b/device/bluetooth/test/mock_bluetooth_discovery_session.cc
@@ -15,8 +15,10 @@
 // test code.
 MockBluetoothDiscoverySession::MockBluetoothDiscoverySession()
     : BluetoothDiscoverySession(
-        scoped_refptr<BluetoothAdapter>(
-            new testing::NiceMock<MockBluetoothAdapter>())) {}
+          scoped_refptr<BluetoothAdapter>(
+              new testing::NiceMock<MockBluetoothAdapter>()),
+          nullptr) {
+}
 MockBluetoothDiscoverySession::~MockBluetoothDiscoverySession() {}
 
 }  // namespace device
diff --git a/device/device_tests.gyp b/device/device_tests.gyp
index 9bf39ac..8b1545a0 100644
--- a/device/device_tests.gyp
+++ b/device/device_tests.gyp
@@ -40,6 +40,7 @@
         'bluetooth/bluetooth_chromeos_unittest.cc',
         'bluetooth/bluetooth_device_unittest.cc',
         'bluetooth/bluetooth_device_win_unittest.cc',
+        'bluetooth/bluetooth_discovery_filter_unittest.cc',
         'bluetooth/bluetooth_gatt_chromeos_unittest.cc',
         'bluetooth/bluetooth_low_energy_win_unittest.cc',
         'bluetooth/bluetooth_service_record_win_unittest.cc',
diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc
index fb12d96e..081e668 100644
--- a/device/hid/hid_service_mac.cc
+++ b/device/hid/hid_service_mac.cc
@@ -142,9 +142,17 @@
 
   base::ScopedCFTypeRef<CFDictionaryRef> matching_dict(
       IORegistryEntryIDMatching(device_id));
+  if (!matching_dict.get()) {
+    HID_LOG(EVENT) << "Failed to create matching dictionary for ID: "
+                   << device_id;
+    task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
+    return;
+  }
 
-  base::mac::ScopedIOObject<io_service_t> service(
-      IOServiceGetMatchingService(kIOMasterPortDefault, matching_dict.get()));
+  // IOServiceGetMatchingService consumes a reference to the matching dictionary
+  // passed to it.
+  base::mac::ScopedIOObject<io_service_t> service(IOServiceGetMatchingService(
+      kIOMasterPortDefault, matching_dict.release()));
   if (!service.get()) {
     HID_LOG(EVENT) << "IOService not found for ID: " << device_id;
     task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 9907815..2992c79e 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -223,6 +223,7 @@
     "channel": "stable",
     "contexts": ["webui"],
     "matches": [
+      "chrome://md-settings/*",
       "chrome://network/*",
       "chrome://settings/*",
       "chrome://settings-frame/*"
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index c61b1889..46abc432 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -365,7 +365,8 @@
     return;
 
   context->DispatchOnUnloadEvent();
-  // TODO(kalman): add an invalidation observer interface to ScriptContext.
+  // TODO(kalman): Make |request_sender| use |context->AddInvalidationObserver|.
+  // In fact |request_sender_| should really be owned by ScriptContext.
   request_sender_->InvalidateSource(context);
 
   script_context_set_.Remove(context);
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index 8a005aa..472f369 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -5,9 +5,6 @@
 #include "extensions/renderer/event_bindings.h"
 
 #include <map>
-#include <set>
-#include <string>
-#include <vector>
 
 #include "base/basictypes.h"
 #include "base/bind.h"
@@ -24,9 +21,7 @@
 #include "extensions/common/value_counter.h"
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/extension_helper.h"
-#include "extensions/renderer/object_backed_native_handler.h"
 #include "url/gurl.h"
-#include "v8/include/v8.h"
 
 namespace extensions {
 
@@ -143,12 +138,10 @@
 
 EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context)
     : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) {
-  RouteFunction(
-      "AttachEvent",
-      base::Bind(&EventBindings::AttachEvent, base::Unretained(this)));
-  RouteFunction(
-      "DetachEvent",
-      base::Bind(&EventBindings::DetachEvent, base::Unretained(this)));
+  RouteFunction("AttachEvent", base::Bind(&EventBindings::AttachEventHandler,
+                                          base::Unretained(this)));
+  RouteFunction("DetachEvent", base::Bind(&EventBindings::DetachEventHandler,
+                                          base::Unretained(this)));
   RouteFunction(
       "AttachFilteredEvent",
       base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this)));
@@ -158,21 +151,36 @@
   RouteFunction("MatchAgainstEventFilter",
                 base::Bind(&EventBindings::MatchAgainstEventFilter,
                            base::Unretained(this)));
+
+  // It's safe to use base::Unretained here because |context| will always
+  // outlive us.
+  context->AddInvalidationObserver(
+      base::Bind(&EventBindings::OnInvalidated, base::Unretained(this)));
 }
 
 EventBindings::~EventBindings() {}
 
-// Attach an event name to an object.
-void EventBindings::AttachEvent(
+void EventBindings::AttachEventHandler(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK_EQ(1, args.Length());
   CHECK(args[0]->IsString());
+  AttachEvent(*v8::String::Utf8Value(args[0]));
+}
 
-  std::string event_name = *v8::String::Utf8Value(args[0]);
-
+void EventBindings::AttachEvent(const std::string& event_name) {
+  // This method throws an exception if it returns false.
   if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context()))
     return;
 
+  // Record the attachment for this context so that events can be detached when
+  // the context is destroyed.
+  //
+  // Ideally we'd CHECK that it's not already attached, however that's not
+  // possible because extensions can create and attach events themselves. Very
+  // silly, but that's the way it is. For an example of this, see
+  // chrome/test/data/extensions/api_test/events/background.js.
+  attached_event_names_.insert(event_name);
+
   const std::string& extension_id = context()->GetExtensionID();
   if (IncrementEventListenerCount(context(), event_name) == 1) {
     content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener(
@@ -189,16 +197,20 @@
   }
 }
 
-void EventBindings::DetachEvent(
+void EventBindings::DetachEventHandler(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK_EQ(2, args.Length());
   CHECK(args[0]->IsString());
   CHECK(args[1]->IsBoolean());
+  DetachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue());
+}
 
-  std::string event_name = *v8::String::Utf8Value(args[0]);
-  bool is_manual = args[1]->BooleanValue();
+void EventBindings::DetachEvent(const std::string& event_name, bool is_manual) {
+  // See comment in AttachEvent().
+  attached_event_names_.erase(event_name);
 
   const std::string& extension_id = context()->GetExtensionID();
+
   if (DecrementEventListenerCount(context(), event_name) == 0) {
     content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener(
         extension_id, context()->GetURL(), event_name));
@@ -324,4 +336,15 @@
       context()->GetRenderView()->GetRoutingID()));
 }
 
+void EventBindings::OnInvalidated() {
+  // Detach all attached events that weren't attached. Iterate over a copy
+  // because it will be mutated.
+  std::set<std::string> attached_event_names_safe = attached_event_names_;
+  for (const std::string& event_name : attached_event_names_safe) {
+    DetachEvent(event_name, false /* is_manual */);
+  }
+  DCHECK(attached_event_names_.empty())
+      << "Events cannot be attached during invalidation";
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/event_bindings.h b/extensions/renderer/event_bindings.h
index 0f69cd3..f5cfdcf 100644
--- a/extensions/renderer/event_bindings.h
+++ b/extensions/renderer/event_bindings.h
@@ -5,6 +5,10 @@
 #ifndef EXTENSIONS_RENDERER_EVENT_BINDINGS_H_
 #define EXTENSIONS_RENDERER_EVENT_BINDINGS_H_
 
+#include <set>
+#include <string>
+
+#include "base/macros.h"
 #include "extensions/renderer/object_backed_native_handler.h"
 #include "v8/include/v8.h"
 
@@ -14,8 +18,6 @@
 
 namespace extensions {
 class Dispatcher;
-class EventFilter;
-class EventFilteringInfo;
 class EventMatcher;
 
 // This class deals with the javascript bindings related to Event objects.
@@ -25,16 +27,25 @@
   ~EventBindings() override;
 
  private:
+  // JavaScript handler which forwards to AttachEvent().
+  // args[0] forwards to |event_name|.
+  void AttachEventHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
+
   // Attach an event name to an object.
   // |event_name| The name of the event to attach.
-  void AttachEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
+  void AttachEvent(const std::string& event_name);
 
-  // Detach an event name from an object.
+  // JavaScript handler which forwards to DetachEvent().
+  // args[0] forwards to |event_name|.
+  // args[1] forwards to |is_manual|.
+  void DetachEventHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  // Detaches an event name from an object.
   // |event_name| The name of the event to stop listening to.
   // |is_manual| True if this detach was done by the user via removeListener()
   // as opposed to automatically during shutdown, in which case we should inform
   // the browser we are no longer interested in that event.
-  void DetachEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
+  void DetachEvent(const std::string& event_name, bool is_manual);
 
   // MatcherID AttachFilteredEvent(string event_name, object filter)
   // |event_name| Name of the event to attach.
@@ -51,9 +62,19 @@
 
   void MatchAgainstEventFilter(const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  Dispatcher* dispatcher_;
   scoped_ptr<EventMatcher> ParseEventMatcher(
       base::DictionaryValue* filter_dict);
+
+  // Called when our context, and therefore us, is invalidated. Run any cleanup.
+  void OnInvalidated();
+
+  // The set of attached events. Maintain this so that we can detch them on
+  // unload.
+  std::set<std::string> attached_event_names_;
+
+  Dispatcher* dispatcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(EventBindings);
 };
 
 }  // namespace extensions
diff --git a/extensions/renderer/event_unittest.cc b/extensions/renderer/event_unittest.cc
index 4144a01..9d3b68f 100644
--- a/extensions/renderer/event_unittest.cc
+++ b/extensions/renderer/event_unittest.cc
@@ -18,7 +18,6 @@
     env()->RegisterModule(kSchemaUtils, IDR_SCHEMA_UTILS_JS);
     env()->RegisterModule("uncaught_exception_handler",
                           IDR_UNCAUGHT_EXCEPTION_HANDLER_JS);
-    env()->RegisterModule("unload_event", IDR_UNLOAD_EVENT_JS);
     env()->RegisterModule("utils", IDR_UTILS_JS);
 
     // Mock out the native handler for event_bindings. These mocks will fail if
@@ -92,41 +91,6 @@
   env()->module_system()->Require("test");
 }
 
-TEST_F(EventUnittest, OnUnloadDetachesAllListeners) {
-  ModuleSystem::NativesEnabledScope natives_enabled_scope(
-      env()->module_system());
-  env()->RegisterModule(
-      "test",
-      "var assert = requireNative('assert');"
-      "var Event = require('event_bindings').Event;"
-      "var eventNatives = requireNative('event_natives');"
-      "var myEvent = new Event('named-event');"
-      "var cb1 = function() {};"
-      "var cb2 = function() {};"
-      "myEvent.addListener(cb1);"
-      "myEvent.addListener(cb2);"
-      "require('unload_event').dispatch();"
-      "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
-  env()->module_system()->Require("test");
-}
-
-TEST_F(EventUnittest, OnUnloadDetachesAllListenersEvenDupes) {
-  ModuleSystem::NativesEnabledScope natives_enabled_scope(
-      env()->module_system());
-  env()->RegisterModule(
-      "test",
-      "var assert = requireNative('assert');"
-      "var Event = require('event_bindings').Event;"
-      "var eventNatives = requireNative('event_natives');"
-      "var myEvent = new Event('named-event');"
-      "var cb1 = function() {};"
-      "myEvent.addListener(cb1);"
-      "myEvent.addListener(cb1);"
-      "require('unload_event').dispatch();"
-      "assert.AssertFalse(!!eventNatives.attachedListeners['named-event']);");
-  env()->module_system()->Require("test");
-}
-
 TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(
       env()->module_system());
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 6194b8e..6a120749 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -151,12 +152,10 @@
   }
 }
 
-ModuleSystem::~ModuleSystem() { Invalidate(); }
+ModuleSystem::~ModuleSystem() {
+}
 
 void ModuleSystem::Invalidate() {
-  if (!is_valid())
-    return;
-
   // Clear the module system properties from the global context. It's polite,
   // and we use this as a signal in lazy handlers that we no longer exist.
   {
@@ -168,12 +167,11 @@
         v8::String::NewFromUtf8(GetIsolate(), kModuleSystem));
   }
 
-  // Invalidate all of the successfully required handlers we own.
-  for (NativeHandlerMap::iterator it = native_handler_map_.begin();
-       it != native_handler_map_.end();
-       ++it) {
-    it->second->Invalidate();
-  }
+  // Invalidate all active and clobbered NativeHandlers we own.
+  for (const auto& handler : native_handler_map_)
+    handler.second->Invalidate();
+  for (const auto& clobbered_handler : clobbered_native_handlers_)
+    clobbered_handler->Invalidate();
 
   ObjectBackedNativeHandler::Invalidate();
 }
@@ -301,11 +299,13 @@
 void ModuleSystem::RegisterNativeHandler(
     const std::string& name,
     scoped_ptr<NativeHandler> native_handler) {
+  ClobberExistingNativeHandler(name);
   native_handler_map_[name] =
       linked_ptr<NativeHandler>(native_handler.release());
 }
 
 void ModuleSystem::OverrideNativeHandlerForTest(const std::string& name) {
+  ClobberExistingNativeHandler(name);
   overridden_native_handlers_.insert(name);
 }
 
@@ -685,4 +685,12 @@
   resolver_local->Resolve(value);
 }
 
+void ModuleSystem::ClobberExistingNativeHandler(const std::string& name) {
+  NativeHandlerMap::iterator existing_handler = native_handler_map_.find(name);
+  if (existing_handler != native_handler_map_.end()) {
+    clobbered_native_handlers_.push_back(existing_handler->second);
+    native_handler_map_.erase(existing_handler);
+  }
+}
+
 }  // namespace extensions
diff --git a/extensions/renderer/module_system.h b/extensions/renderer/module_system.h
index 8b4a35c..ad89524e 100644
--- a/extensions/renderer/module_system.h
+++ b/extensions/renderer/module_system.h
@@ -142,6 +142,7 @@
   }
 
  protected:
+  friend class ModuleSystemTestEnvironment;
   friend class ScriptContext;
   void Invalidate() override;
 
@@ -205,6 +206,10 @@
       const std::string& id,
       const std::vector<std::string>& dependencies) override;
 
+  // Marks any existing NativeHandler named |name| as clobbered.
+  // See |clobbered_native_handlers_|.
+  void ClobberExistingNativeHandler(const std::string& name);
+
   ScriptContext* context_;
 
   // A map from module names to the JS source for that module. GetSource()
@@ -222,8 +227,16 @@
   // tests.
   scoped_ptr<ExceptionHandler> exception_handler_;
 
+  // A set of native handlers that should actually be require()d as non-native
+  // handlers. This is used for tests to mock out native handlers in JS.
   std::set<std::string> overridden_native_handlers_;
 
+  // A list of NativeHandlers that have been clobbered, either due to
+  // registering a NativeHandler when one was already registered with the same
+  // name, or due to OverrideNativeHandlerForTest. This is needed so that they
+  // can be later Invalidated. It should only happen in tests.
+  std::vector<linked_ptr<NativeHandler>> clobbered_native_handlers_;
+
   base::WeakPtrFactory<ModuleSystem> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
diff --git a/extensions/renderer/module_system_test.cc b/extensions/renderer/module_system_test.cc
index b9d4a4d..cc4cbfba 100644
--- a/extensions/renderer/module_system_test.cc
+++ b/extensions/renderer/module_system_test.cc
@@ -157,7 +157,7 @@
 
 ModuleSystemTestEnvironment::~ModuleSystemTestEnvironment() {
   if (context_->is_valid())
-    context_->v8_context()->Exit();
+    ShutdownModuleSystem();
 }
 
 void ModuleSystemTestEnvironment::RegisterModule(const std::string& name,
@@ -196,6 +196,7 @@
 }
 
 void ModuleSystemTestEnvironment::ShutdownModuleSystem() {
+  CHECK(context_->is_valid());
   context_->v8_context()->Exit();
   context_->Invalidate();
 }
diff --git a/extensions/renderer/native_handler.cc b/extensions/renderer/native_handler.cc
index 896ef56..a8b343a 100644
--- a/extensions/renderer/native_handler.cc
+++ b/extensions/renderer/native_handler.cc
@@ -4,12 +4,19 @@
 
 #include "extensions/renderer/native_handler.h"
 
+#include "base/logging.h"
+
 namespace extensions {
 
 NativeHandler::NativeHandler() : is_valid_(true) {}
 
-NativeHandler::~NativeHandler() {}
+NativeHandler::~NativeHandler() {
+  CHECK(!is_valid_) << "NativeHandlers must be invalidated before destruction";
+}
 
-void NativeHandler::Invalidate() { is_valid_ = false; }
+void NativeHandler::Invalidate() {
+  CHECK(is_valid_);
+  is_valid_ = false;
+}
 
 }  // namespace extensions
diff --git a/extensions/renderer/native_handler.h b/extensions/renderer/native_handler.h
index c0660c8..1491feb8 100644
--- a/extensions/renderer/native_handler.h
+++ b/extensions/renderer/native_handler.h
@@ -29,6 +29,9 @@
   //
   // Subclasses should override to invalidate their own V8 state. If they do
   // they must call their superclass' Invalidate().
+  //
+  // Invalidate() will be called on destruction, if it hasn't already been.
+  // Subclasses don't need to do it themselves.
   virtual void Invalidate();
 
  protected:
diff --git a/extensions/renderer/object_backed_native_handler.cc b/extensions/renderer/object_backed_native_handler.cc
index 10bedc1..075f1d6 100644
--- a/extensions/renderer/object_backed_native_handler.cc
+++ b/extensions/renderer/object_backed_native_handler.cc
@@ -25,7 +25,8 @@
                        v8::ObjectTemplate::New(context->isolate())) {
 }
 
-ObjectBackedNativeHandler::~ObjectBackedNativeHandler() { Invalidate(); }
+ObjectBackedNativeHandler::~ObjectBackedNativeHandler() {
+}
 
 v8::Handle<v8::Object> ObjectBackedNativeHandler::NewInstance() {
   return v8::Local<v8::ObjectTemplate>::New(GetIsolate(), object_template_)
@@ -75,9 +76,7 @@
 }
 
 void ObjectBackedNativeHandler::Invalidate() {
-  if (!is_valid())
-    return;
-  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::Isolate* isolate = GetIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Context::Scope context_scope(context_->v8_context());
 
@@ -90,9 +89,10 @@
         handler_function_value.As<v8::External>()->Value());
     data->Delete(v8::String::NewFromUtf8(isolate, kHandlerFunction));
   }
+
   router_data_.Clear();
   object_template_.Reset();
-  context_ = NULL;
+
   NativeHandler::Invalidate();
 }
 
diff --git a/extensions/renderer/object_backed_native_handler.h b/extensions/renderer/object_backed_native_handler.h
index 7fe4b1fe3..843e13b 100644
--- a/extensions/renderer/object_backed_native_handler.h
+++ b/extensions/renderer/object_backed_native_handler.h
@@ -38,6 +38,10 @@
   // Installs a new 'route' from |name| to |handler_function|. This means that
   // NewInstance()s of this ObjectBackedNativeHandler will have a property
   // |name| which will be handled by |handler_function|.
+  //
+  // Routed functions are destroyed along with the destruction of this class,
+  // and are never called back into, therefore it's safe for |handler_function|
+  // to bind to base::Unretained.
   void RouteFunction(const std::string& name,
                      const HandlerFunction& handler_function);
 
diff --git a/extensions/renderer/resources/event.js b/extensions/renderer/resources/event.js
index f62ab0f..bb71297 100644
--- a/extensions/renderer/resources/event.js
+++ b/extensions/renderer/resources/event.js
@@ -9,7 +9,6 @@
   var sendRequest = require('sendRequest').sendRequest;
   var utils = require('utils');
   var validate = require('schemaUtils').validate;
-  var unloadEvent = require('unload_event');
 
   // Schemas for the rule-style functions on the events API that
   // only need to be generated occasionally, so populate them lazily.
@@ -38,9 +37,6 @@
   // A map of event names to the event object that is registered to that name.
   var attachedNamedEvents = {};
 
-  // An array of all attached event objects, used for detaching on unload.
-  var allAttachedEvents = [];
-
   // A map of functions that massage event arguments before they are dispatched.
   // Key is event name, value is function.
   var eventArgumentMassagers = {};
@@ -283,7 +279,6 @@
     this.attachmentStrategy.onAddedListener(listener);
 
     if (this.listeners.length == 0) {
-      allAttachedEvents[allAttachedEvents.length] = this;
       if (this.eventName) {
         if (attachedNamedEvents[this.eventName]) {
           throw new Error("Event '" + this.eventName +
@@ -307,9 +302,6 @@
     this.attachmentStrategy.onRemovedListener(removedListener);
 
     if (this.listeners.length == 0) {
-      var i = $Array.indexOf(allAttachedEvents, this);
-      if (i >= 0)
-        delete allAttachedEvents[i];
       if (this.eventName) {
         if (!attachedNamedEvents[this.eventName]) {
           throw new Error(
@@ -498,14 +490,6 @@
       ruleFunctionSchemas.getRules.parameters);
   }
 
-  unloadEvent.addListener(function() {
-    for (var i = 0; i < allAttachedEvents.length; ++i) {
-      var event = allAttachedEvents[i];
-      if (event)
-        event.detach_();
-    }
-  });
-
   var Event = utils.expose('Event', EventImpl, { functions: [
     'addListener',
     'removeListener',
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index e5677aa..c3f04fa 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -80,7 +80,8 @@
                              Feature::Context context_type,
                              const Extension* effective_extension,
                              Feature::Context effective_context_type)
-    : v8_context_(v8_context->GetIsolate(), v8_context),
+    : is_valid_(true),
+      v8_context_(v8_context->GetIsolate(), v8_context),
       web_frame_(web_frame),
       extension_(extension),
       context_type_(context_type),
@@ -107,17 +108,34 @@
           << "  extension id: " << GetExtensionID() << "\n"
           << "  effective extension id: "
           << (effective_extension_.get() ? effective_extension_->id() : "");
-  Invalidate();
+  CHECK(!is_valid_) << "ScriptContexts must be invalidated before destruction";
 }
 
 void ScriptContext::Invalidate() {
-  if (!is_valid())
-    return;
+  CHECK(is_valid_);
+  is_valid_ = false;
+
+  // TODO(kalman): Make ModuleSystem use AddInvalidationObserver.
+  // Ownership graph is a bit weird here.
   if (module_system_)
     module_system_->Invalidate();
-  web_frame_ = NULL;
-  v8_context_.Reset();
+
+  // Swap |invalidate_observers_| to a local variable to clear it, and to make
+  // sure it's not mutated as we iterate.
+  std::vector<base::Closure> observers;
+  observers.swap(invalidate_observers_);
+  for (const base::Closure& observer : observers) {
+    observer.Run();
+  }
+  DCHECK(invalidate_observers_.empty())
+      << "Invalidation observers cannot be added during invalidation";
+
   runner_.reset();
+  v8_context_.Reset();
+}
+
+void ScriptContext::AddInvalidationObserver(const base::Closure& observer) {
+  invalidate_observers_.push_back(observer);
 }
 
 const std::string& ScriptContext::GetExtensionID() const {
@@ -144,7 +162,7 @@
   v8::Context::Scope scope(v8_context());
 
   blink::WebScopedMicrotaskSuppression suppression;
-  if (!is_valid()) {
+  if (!is_valid_) {
     return handle_scope.Escape(
         v8::Local<v8::Primitive>(v8::Undefined(isolate())));
   }
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index b6c90f2..10901e3 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -6,8 +6,10 @@
 #define EXTENSIONS_RENDERER_SCRIPT_CONTEXT_H_
 
 #include <string>
+#include <vector>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "extensions/common/features/feature.h"
 #include "extensions/common/permissions/api_permission_set.h"
@@ -45,9 +47,13 @@
   // ModuleSystem.
   void Invalidate();
 
+  // Registers |observer| to be run when this context is invalidated. Closures
+  // are run immediately when Invalidate() is called, not in a message loop.
+  void AddInvalidationObserver(const base::Closure& observer);
+
   // Returns true if this context is still valid, false if it isn't.
   // A context becomes invalid via Invalidate().
-  bool is_valid() const { return !v8_context_.IsEmpty(); }
+  bool is_valid() const { return is_valid_; }
 
   v8::Handle<v8::Context> v8_context() const {
     return v8::Local<v8::Context>::New(isolate_, v8_context_);
@@ -150,13 +156,15 @@
   // extension.
   bool HasAPIPermission(APIPermission::ID permission) const;
 
- protected:
-  // The v8 context the bindings are accessible to.
-  v8::Global<v8::Context> v8_context_;
-
  private:
   class Runner;
 
+  // Whether this context is valid.
+  bool is_valid_;
+
+  // The v8 context the bindings are accessible to.
+  v8::Global<v8::Context> v8_context_;
+
   // The WebFrame associated with this context. This can be NULL because this
   // object can outlive is destroyed asynchronously.
   blink::WebFrame* web_frame_;
@@ -185,6 +193,10 @@
   // The set of capabilities granted to this context by extensions.
   APIPermissionSet content_capabilities_;
 
+  // A list of base::Closure instances as an observer interface for
+  // invalidation.
+  std::vector<base::Closure> invalidate_observers_;
+
   v8::Isolate* isolate_;
 
   GURL url_;
diff --git a/extensions/renderer/script_context_set_unittest.cc b/extensions/renderer/script_context_set_unittest.cc
index bf7c38d..cbc0a76c 100644
--- a/extensions/renderer/script_context_set_unittest.cc
+++ b/extensions/renderer/script_context_set_unittest.cc
@@ -53,7 +53,7 @@
   EXPECT_EQ(1u, set_copy.size());
 
   // After removal, the context should be marked for destruction.
-  EXPECT_FALSE(context->web_frame());
+  EXPECT_FALSE(context->is_valid());
 
   // Run loop to do the actual deletion.
   loop.RunUntilIdle();
diff --git a/extensions/renderer/v8_schema_registry.cc b/extensions/renderer/v8_schema_registry.cc
index 841c8cf..c08ca47 100644
--- a/extensions/renderer/v8_schema_registry.cc
+++ b/extensions/renderer/v8_schema_registry.cc
@@ -29,6 +29,8 @@
                              base::Unretained(this)));
   }
 
+  ~SchemaRegistryNativeHandler() override { context_->Invalidate(); }
+
  private:
   void GetSchema(const v8::FunctionCallbackInfo<v8::Value>& args) {
     args.GetReturnValue().Set(
diff --git a/google_apis/drive/drive_api_url_generator.cc b/google_apis/drive/drive_api_url_generator.cc
index 285e754..f2d36fa 100644
--- a/google_apis/drive/drive_api_url_generator.cc
+++ b/google_apis/drive/drive_api_url_generator.cc
@@ -29,6 +29,7 @@
 const char kDriveV2FileTrashUrlFormat[] = "drive/v2/files/%s/trash";
 const char kDriveV2UploadNewFileUrl[] = "upload/drive/v2/files";
 const char kDriveV2UploadExistingFileUrlPrefix[] = "upload/drive/v2/files/";
+const char kDriveV2BatchUploadUrl[] = "upload/drive";
 const char kDriveV2PermissionsUrlFormat[] = "drive/v2/files/%s/permissions";
 const char kDriveV2DownloadUrlFormat[] = "host/%s";
 const char kDriveV2ThumbnailUrlFormat[] = "thumb/%s?width=%d&height=%d";
@@ -288,4 +289,8 @@
                          net::EscapePath(resource_id).c_str(), width, height));
 }
 
+GURL DriveApiUrlGenerator::GetBatchUploadUrl() const {
+  return base_url_.Resolve(kDriveV2BatchUploadUrl);
+}
+
 }  // namespace google_apis
diff --git a/google_apis/drive/drive_api_url_generator.h b/google_apis/drive/drive_api_url_generator.h
index ab623e30..dbdc6c7 100644
--- a/google_apis/drive/drive_api_url_generator.h
+++ b/google_apis/drive/drive_api_url_generator.h
@@ -112,6 +112,9 @@
                        int width,
                        int height) const;
 
+  // Generates a URL for batch upload.
+  GURL GetBatchUploadUrl() const;
+
  private:
   const GURL base_url_;
   const GURL base_download_url_;
diff --git a/google_apis/drive/drive_api_url_generator_unittest.cc b/google_apis/drive/drive_api_url_generator_unittest.cc
index 03e4c02d..92e128e 100644
--- a/google_apis/drive/drive_api_url_generator_unittest.cc
+++ b/google_apis/drive/drive_api_url_generator_unittest.cc
@@ -363,4 +363,10 @@
       url_generator_.GetThumbnailUrl("0ADK06pfg", 500, 500).spec());
 }
 
+TEST_F(DriveApiUrlGeneratorTest, BatchUploadUrl) {
+  EXPECT_EQ(
+      "https://www.example.com/upload/drive",
+      url_generator_.GetBatchUploadUrl().spec());
+}
+
 }  // namespace google_apis
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index 5be2eb6..ec2fdea 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -7,6 +7,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/sparse_histogram.h"
+#include "base/profiler/scoped_tracker.h"
 #include "google_apis/gcm/engine/connection_handler_impl.h"
 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
 #include "google_apis/gcm/protocol/mcs.pb.h"
@@ -349,6 +350,10 @@
 }
 
 void ConnectionFactoryImpl::OnConnectDone(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 ConnectionFactoryImpl::OnConnectDone"));
   if (result != net::OK) {
     // If the connection fails, try another proxy.
     result = ReconsiderProxyAfterError(result);
diff --git a/gpu/DEPS b/gpu/DEPS
index 6b8aacb..1b002bcc 100644
--- a/gpu/DEPS
+++ b/gpu/DEPS
@@ -8,4 +8,5 @@
   "+ui/gfx",
   "+ui/gl",
   "+ui/surface",
+  "+ui/ozone/public",
 ]
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
index ac83da5..6348abb 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/linked_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/gpu_scheduler.h"
@@ -17,10 +16,6 @@
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_nsautorelease_pool.h"
-#endif
-
 namespace gpu {
 
 using testing::Return;
@@ -256,10 +251,6 @@
 
   CommandBufferOffset get_helper_put() { return helper_->put_; }
 
-#if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool autorelease_pool_;
-#endif
-  base::MessageLoop message_loop_;
   scoped_ptr<AsyncAPIMock> api_mock_;
   scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
   scoped_ptr<CommandBufferServiceLocked> command_buffer_;
diff --git a/gpu/command_buffer/client/fenced_allocator_test.cc b/gpu/command_buffer/client/fenced_allocator_test.cc
index 3af9367..6b9f00f4 100644
--- a/gpu/command_buffer/client/fenced_allocator_test.cc
+++ b/gpu/command_buffer/client/fenced_allocator_test.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/aligned_memory.h"
-#include "base/message_loop/message_loop.h"
 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
 #include "gpu/command_buffer/client/fenced_allocator.h"
 #include "gpu/command_buffer/service/cmd_buffer_engine.h"
@@ -17,10 +16,6 @@
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_nsautorelease_pool.h"
-#endif
-
 namespace gpu {
 
 using testing::Return;
@@ -74,10 +69,6 @@
     return command_buffer_->GetLastState().token;
   }
 
-#if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool autorelease_pool_;
-#endif
-  base::MessageLoop message_loop_;
   scoped_ptr<AsyncAPIMock> api_mock_;
   scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
   scoped_ptr<CommandBufferService> command_buffer_;
diff --git a/gpu/command_buffer/client/mapped_memory_unittest.cc b/gpu/command_buffer/client/mapped_memory_unittest.cc
index 9d5bada..6430b667 100644
--- a/gpu/command_buffer/client/mapped_memory_unittest.cc
+++ b/gpu/command_buffer/client/mapped_memory_unittest.cc
@@ -15,10 +15,6 @@
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_nsautorelease_pool.h"
-#endif
-
 namespace gpu {
 
 using testing::Return;
@@ -71,10 +67,6 @@
     return command_buffer_->GetLastState().token;
   }
 
-#if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool autorelease_pool_;
-#endif
-  base::MessageLoop message_loop_;
   scoped_ptr<AsyncAPIMock> api_mock_;
   scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
   scoped_ptr<CommandBufferService> command_buffer_;
diff --git a/gpu/command_buffer/client/ring_buffer_test.cc b/gpu/command_buffer/client/ring_buffer_test.cc
index c1aab88..ac5634a 100644
--- a/gpu/command_buffer/client/ring_buffer_test.cc
+++ b/gpu/command_buffer/client/ring_buffer_test.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
 #include "gpu/command_buffer/service/cmd_buffer_engine.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
@@ -17,10 +16,6 @@
 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_nsautorelease_pool.h"
-#endif
-
 namespace gpu {
 
 using testing::Return;
@@ -96,10 +91,6 @@
     return command_buffer_->GetLastState().token;
   }
 
-#if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool autorelease_pool_;
-#endif
-  base::MessageLoop message_loop_;
   scoped_ptr<AsyncAPIMock> api_mock_;
   scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
   scoped_ptr<CommandBufferService> command_buffer_;
diff --git a/gpu/command_buffer/common/unittest_main.cc b/gpu/command_buffer/common/unittest_main.cc
index 874bae7..17b6114 100644
--- a/gpu/command_buffer/common/unittest_main.cc
+++ b/gpu/command_buffer/common/unittest_main.cc
@@ -5,11 +5,16 @@
 #include "base/at_exit.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
 namespace {
 
 class NoAtExitBaseTestSuite : public base::TestSuite {
@@ -20,6 +25,7 @@
 };
 
 int RunTestSuite(int argc, char** argv) {
+  base::MessageLoop message_loop;
   return NoAtExitBaseTestSuite(argc, argv).Run();
 }
 
@@ -33,6 +39,9 @@
   base::AtExitManager exit_manager;
 #endif
   base::CommandLine::Init(argc, argv);
+#if defined(OS_MACOSX)
+  base::mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
   testing::InitGoogleMock(&argc, argv);
   return base::LaunchUnitTests(argc,
                                argv,
diff --git a/gpu/command_buffer/service/gpu_scheduler_unittest.cc b/gpu/command_buffer/service/gpu_scheduler_unittest.cc
index c1c0d1c..c1c2ef7 100644
--- a/gpu/command_buffer/service/gpu_scheduler_unittest.cc
+++ b/gpu/command_buffer/service/gpu_scheduler_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/message_loop/message_loop.h"
 #include "gpu/command_buffer/common/command_buffer_mock.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
@@ -11,10 +10,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MACOSX)
-#include "base/mac/scoped_nsautorelease_pool.h"
-#endif
-
 using testing::_;
 using testing::DoAll;
 using testing::Invoke;
@@ -72,10 +67,6 @@
     return command_buffer_->GetLastState().error;
   }
 
-#if defined(OS_MACOSX)
-  base::mac::ScopedNSAutoreleasePool autorelease_pool_;
-#endif
-  base::MessageLoop message_loop;
   scoped_ptr<MockCommandBuffer> command_buffer_;
   scoped_refptr<Buffer> shared_memory_buffer_;
   int32* buffer_;
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index e4ed83d5..0196e3b 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -302,7 +302,7 @@
 
   decoder_->set_engine(gpu_scheduler_.get());
 
-  surface_ = gfx::GLSurface::CreateOffscreenGLSurface(options.size);
+  surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size());
   ASSERT_TRUE(surface_.get() != NULL) << "could not create offscreen surface";
 
   if (base_context_) {
diff --git a/gpu/command_buffer/tests/gl_tests_main.cc b/gpu/command_buffer/tests/gl_tests_main.cc
index 7b7d8d86..6ac5719 100644
--- a/gpu/command_buffer/tests/gl_tests_main.cc
+++ b/gpu/command_buffer/tests/gl_tests_main.cc
@@ -24,7 +24,14 @@
 namespace {
 
 int RunHelper(base::TestSuite* testSuite) {
+#if defined(USE_OZONE)
+  base::MessageLoopForUI main_loop;
+#else
   base::MessageLoopForIO message_loop;
+#endif
+  gfx::GLSurface::InitializeOneOff();
+  ::gles2::Initialize();
+  gpu::ApplyGpuDriverBugWorkarounds(base::CommandLine::ForCurrentProcess());
   return testSuite->Run();
 }
 
@@ -39,9 +46,6 @@
 #if defined(OS_MACOSX)
   base::mac::ScopedNSAutoreleasePool pool;
 #endif
-  gfx::GLSurface::InitializeOneOff();
-  ::gles2::Initialize();
-  gpu::ApplyGpuDriverBugWorkarounds(base::CommandLine::ForCurrentProcess());
   testing::InitGoogleMock(&argc, argv);
   return base::LaunchUnitTestsSerially(
       argc,
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index 7d3d890..0a9a8d8 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -18,7 +18,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "10.3",
+  "version": "10.4",
   "entries": [
     {
       "id": 1,
@@ -331,6 +331,11 @@
       },
       "vendor_id": "0x10de",
       "gl_vendor": "(?i)nouveau.*",
+      "driver_vendor": "Mesa",
+      "driver_version": {
+        "op": "<",
+        "value": "10.1"
+      },
       "features": [
         "all"
       ]
@@ -349,17 +354,6 @@
       ]
     },
     {
-      "id": 35,
-      "description": "Stage3D is not supported on Linux",
-      "cr_bugs": [129848],
-      "os": {
-        "type": "linux"
-      },
-      "features": [
-        "flash_stage3d"
-      ]
-    },
-    {
       "id": 37,
       "description": "Older drivers are unreliable for Optimus on Linux",
       "cr_bugs": [131308, 363418],
diff --git a/ios/chrome/DEPS b/ios/chrome/DEPS
index f23eea2a..6503a0e 100644
--- a/ios/chrome/DEPS
+++ b/ios/chrome/DEPS
@@ -2,36 +2,4 @@
   # The subdirectories in ios/chrome/ will manually allow their own include
   # directories in ios/chrome/ so we disallow all of them.
   "-ios/chrome",
-  "+ios/chrome/grit",
-
-  "+components/autofill/core/browser",
-  "+components/autofill/ios/browser",
-  "+components/data_reduction_proxy/core/common",
-  "+components/dom_distiller/core",
-  "+components/dom_distiller/ios",
-  "+components/infobars/core",
-  "+components/keyed_service/core",
-  "+components/keyed_service/ios",
-  "+components/leveldb_proto",
-  "+components/suggestions",
-  "+components/translate/core",
-  "+components/translate/ios",
-  "+components/web_resource",
-  "+components/webp_transcode",
-  "+ios/net",
-  "+ios/public/provider/chrome",
-  "+ios/web/public",
-  "+net",
-  "+third_party/google_toolbox_for_mac",
-  "+ui",
-
-  # For tests.
-  "+ios/public/test",
-
-  # Only parts of skia are compiled on iOS, so we explicitly list the
-  # files that can be included to avoid bringing in more code.
-  "+skia/ext/skia_utils_ios.h",
-
-  # Generated file for translated strings in components.
-  "+grit/components_strings.h",
 ]
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
new file mode 100644
index 0000000..f34a686
--- /dev/null
+++ b/ios/chrome/browser/DEPS
@@ -0,0 +1,34 @@
+include_rules = [
+  "+ios/chrome/grit",
+
+  "+components/autofill/core/browser",
+  "+components/autofill/ios/browser",
+  "+components/data_reduction_proxy/core/common",
+  "+components/dom_distiller/core",
+  "+components/dom_distiller/ios",
+  "+components/infobars/core",
+  "+components/keyed_service/core",
+  "+components/keyed_service/ios",
+  "+components/leveldb_proto",
+  "+components/suggestions",
+  "+components/translate/core",
+  "+components/translate/ios",
+  "+components/web_resource",
+  "+components/webp_transcode",
+  "+ios/net",
+  "+ios/public/provider/chrome",
+  "+ios/web/public",
+  "+net",
+  "+third_party/google_toolbox_for_mac",
+  "+ui",
+
+  # For tests.
+  "+ios/public/test",
+
+  # Only parts of skia are compiled on iOS, so we explicitly list the
+  # files that can be included to avoid bringing in more code.
+  "+skia/ext/skia_utils_ios.h",
+
+  # Generated file for translated strings in components.
+  "+grit/components_strings.h",
+]
diff --git a/ios/chrome/common/README.txt b/ios/chrome/common/README.txt
new file mode 100644
index 0000000..72023cf
--- /dev/null
+++ b/ios/chrome/common/README.txt
@@ -0,0 +1,2 @@
+This directory holds code shared by Chrome and Chrome iOS extensions.
+
diff --git a/ios/chrome/common/string_util.h b/ios/chrome/common/string_util.h
new file mode 100644
index 0000000..9176a3a8
--- /dev/null
+++ b/ios/chrome/common/string_util.h
@@ -0,0 +1,44 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_COMMON_STRING_UTIL_H_
+#define IOS_CHROME_COMMON_STRING_UTIL_H_
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <Foundation/Foundation.h>
+#include <string>
+
+// Parses a string with an embedded link inside, delineated by BEGIN_LINK and
+// END_LINK. Returns the string without the link delimiters. If |out_link_range|
+// is not null, then it is filled out with the range of the link in the returned
+// string.
+// If no link is found, then it returns |text| and sets |out_link_range| to
+// {NSNotFound, 0}.
+NSString* ParseStringWithLink(NSString* text, NSRange* out_link_range);
+
+// Utility method that returns an NSCharacterSet containing Unicode graphics
+// and drawing characters (but not including the Braille Patterns characters).
+NSCharacterSet* GraphicCharactersSet();
+
+// Cleans an NSString by collapsing whitespace and (if |trim| is true)
+// removing leading and trailing spaces. If |removeGraphicChars| is true,
+// unicode graphic characters will also be removed from the string.
+NSString* CleanNSStringForDisplay(NSString* dirty,
+                                  BOOL removeGraphicChars,
+                                  BOOL trim);
+
+// Cleans a std::string identically to CleanNSStringForDisplay()
+std::string CleanStringForDisplay(std::string dirty,
+                                  BOOL removeGraphicChars,
+                                  BOOL trim);
+
+// Find the longest leading substring of |string| that, when rendered with
+// |attributes|, will fit on a single line inside |targetWidth|. If |trailing|
+// is YES, then find the trailing (instead of leading) substring.
+NSString* SubstringOfWidth(NSString* string,
+                           NSDictionary* attributes,
+                           CGFloat targetWidth,
+                           BOOL trailing);
+
+#endif  // IOS_CHROME_COMMON_STRING_UTIL_H_
diff --git a/ios/chrome/common/string_util.mm b/ios/chrome/common/string_util.mm
new file mode 100644
index 0000000..1d0510a
--- /dev/null
+++ b/ios/chrome/common/string_util.mm
@@ -0,0 +1,184 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/common/string_util.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_block.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace {
+typedef BOOL (^ArrayFilterProcedure)(id object, NSUInteger index, BOOL* stop);
+typedef NSString* (^SubstringExtractionProcedure)(NSUInteger);
+}
+
+NSString* ParseStringWithLink(NSString* text, NSRange* out_link_range) {
+  // Find the range within |text| and create a substring without the link tags.
+  NSRange begin_range = [text rangeOfString:@"BEGIN_LINK[ \t]*"
+                                    options:NSRegularExpressionSearch];
+  NSRange link_text_range = NSMakeRange(NSNotFound, 0);
+  if (begin_range.length == 0) {
+    if (out_link_range)
+      *out_link_range = link_text_range;
+    return text;
+  }
+
+  NSUInteger after_begin_link = NSMaxRange(begin_range);
+  NSRange range_to_search_for_end_link =
+      NSMakeRange(after_begin_link, text.length - after_begin_link);
+  NSRange end_range = [text rangeOfString:@"[ \t]*END_LINK"
+                                  options:NSRegularExpressionSearch
+                                    range:range_to_search_for_end_link];
+  if (end_range.length == 0) {
+    if (out_link_range)
+      *out_link_range = link_text_range;
+    return text;
+  }
+
+  link_text_range.location = after_begin_link;
+  link_text_range.length = end_range.location - link_text_range.location;
+  base::scoped_nsobject<NSMutableString> out_text(
+      [[NSMutableString alloc] init]);
+  // First part - before the link.
+  if (begin_range.location > 0)
+    [out_text appendString:[text substringToIndex:begin_range.location]];
+
+  // Link part.
+  [out_text appendString:[text substringWithRange:link_text_range]];
+
+  // Last part - after the link.
+  NSUInteger after_end_link = NSMaxRange(end_range);
+  if (after_end_link < [text length]) {
+    [out_text appendString:[text substringFromIndex:after_end_link]];
+  }
+
+  link_text_range.location = begin_range.location;
+  if (out_link_range)
+    *out_link_range = link_text_range;
+  return [NSString stringWithString:out_text];
+}
+
+// Ranges of unicode codepage containing drawing characters.
+// 2190—21FF Arrows
+// 2200—22FF Mathematical Operators
+// 2300—23FF Miscellaneous Technical
+// 2400—243F Control Pictures
+// 2440—245F Optical Character Recognition
+// 2460—24FF Enclosed Alphanumerics
+// 2500—257F Box Drawing
+// 2580—259F Block Elements
+// 25A0—25FF Geometric Shapes
+// 2600—26FF Miscellaneous Symbols
+// 2700—27BF Dingbats
+// 27C0—27EF Miscellaneous Mathematical Symbols-A
+// 27F0—27FF Supplemental Arrows-A
+// 2900—297F Supplemental Arrows-B
+// 2980—29FF Miscellaneous Mathematical Symbols-B
+// 2A00—2AFF Supplemental Mathematical Operators
+// 2B00—2BFF Miscellaneous Symbols and Arrows
+// The section 2800—28FF Braille Patterns must be preserved.
+// The list of characters that must be deleted from the selection.
+NSCharacterSet* GraphicCharactersSet() {
+  static NSMutableCharacterSet* graphicalCharsSet;
+  static dispatch_once_t dispatch_once_token;
+  dispatch_once(&dispatch_once_token, ^{
+    graphicalCharsSet = [[NSMutableCharacterSet alloc] init];
+    NSRange graphicalCharsFirstRange = NSMakeRange(0x2190, 0x2800 - 0x2190);
+    NSRange graphicalCharsSecondRange = NSMakeRange(0x2900, 0x2c00 - 0x2900);
+    [graphicalCharsSet addCharactersInRange:graphicalCharsFirstRange];
+    [graphicalCharsSet addCharactersInRange:graphicalCharsSecondRange];
+  });
+  return graphicalCharsSet;
+}
+
+// TODO(marq): Add unit tests for this function.
+NSString* CleanNSStringForDisplay(NSString* dirty,
+                                  BOOL removeGraphicChars,
+                                  BOOL trim) {
+  NSCharacterSet* wspace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+  NSString* cleanString = dirty;
+  if (removeGraphicChars) {
+    cleanString = [[cleanString
+        componentsSeparatedByCharactersInSet:GraphicCharactersSet()]
+        componentsJoinedByString:@" "];
+  }
+  base::scoped_nsobject<NSMutableArray> spaceSeparatedCompoments(
+      [[cleanString componentsSeparatedByCharactersInSet:wspace] mutableCopy]);
+  ArrayFilterProcedure filter = ^(id object, NSUInteger index, BOOL* stop) {
+    return [object isEqualToString:@""];
+  };
+  [spaceSeparatedCompoments
+      removeObjectsAtIndexes:[spaceSeparatedCompoments
+                                 indexesOfObjectsPassingTest:filter]];
+  cleanString = [spaceSeparatedCompoments componentsJoinedByString:@" "];
+  return trim ? [cleanString stringByTrimmingCharactersInSet:wspace]
+              : cleanString;
+}
+
+std::string CleanStringForDisplay(std::string dirty,
+                                  BOOL removeGraphicChars,
+                                  BOOL trim) {
+  return base::SysNSStringToUTF8(CleanNSStringForDisplay(
+      base::SysUTF8ToNSString(dirty), removeGraphicChars, trim));
+}
+
+// TODO(marq): Add unit tests for this function.
+NSString* SubstringOfWidth(NSString* string,
+                           NSDictionary* attributes,
+                           CGFloat targetWidth,
+                           BOOL trailing) {
+  if (![string length])
+    return nil;
+
+  UIFont* font = [attributes objectForKey:NSFontAttributeName];
+  DCHECK(font);
+
+  // Function to get the correct substring while insulating against
+  // length overrun/underrun.
+  base::mac::ScopedBlock<SubstringExtractionProcedure> getSubstring;
+  if (trailing) {
+    getSubstring.reset([^NSString*(NSUInteger chars) {
+      NSUInteger length = [string length];
+      return [string substringFromIndex:length - MIN(length, chars)];
+    } copy]);
+  } else {
+    getSubstring.reset([^NSString*(NSUInteger chars) {
+      return [string substringToIndex:MIN(chars, [string length])];
+    } copy]);
+  }
+
+  // Guess at the number of characters that will fit, assuming
+  // the font's x-height is about 25% wider than an average character (25%
+  // value was determined experimentally).
+  NSUInteger characters =
+      MIN(targetWidth / (font.xHeight * 0.8), [string length]);
+  NSInteger increment = 1;
+  NSString* substring = getSubstring.get()(characters);
+  CGFloat prevWidth = [substring sizeWithAttributes:attributes].width;
+  do {
+    characters += increment;
+    substring = getSubstring.get()(characters);
+    CGFloat thisWidth = [substring sizeWithAttributes:attributes].width;
+    if (prevWidth > targetWidth) {
+      if (thisWidth < targetWidth)
+        break;  // Shrinking the string, found the right size.
+      else
+        increment = -1;  // Shrink the string
+    } else if (prevWidth < targetWidth) {
+      if (thisWidth < targetWidth)
+        increment = 1;  // Grow the string
+      else {
+        substring = getSubstring.get()(characters - increment);
+        break;  // Growing the string, found the right size.
+      }
+    }
+    prevWidth = thisWidth;
+  } while (characters > 0 && characters < [string length]);
+
+  return substring;
+}
diff --git a/ios/chrome/common/string_util_unittest.mm b/ios/chrome/common/string_util_unittest.mm
new file mode 100644
index 0000000..fb70bec
--- /dev/null
+++ b/ios/chrome/common/string_util_unittest.mm
@@ -0,0 +1,58 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/common/string_util.h"
+
+#include "base/mac/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+TEST(StringUtilTest, ParseStringWithLink) {
+  NSArray* const all_test_data = @[
+    @{
+      @"input" : @"Text without link.",
+      @"expected" : @"Text without link.",
+      @"link range" : [NSValue valueWithRange:NSMakeRange(NSNotFound, 0)]
+    },
+    @{
+      @"input" : @"Text with empty link BEGIN_LINK END_LINK.",
+      @"expected" : @"Text with empty link .",
+      @"link range" : [NSValue valueWithRange:NSMakeRange(21, 0)]
+    },
+    @{
+      @"input" : @"Text with BEGIN_LINK and no end link.",
+      @"expected" : @"Text with BEGIN_LINK and no end link.",
+      @"link range" : [NSValue valueWithRange:NSMakeRange(NSNotFound, 0)]
+    },
+    @{
+      @"input" : @"Text with valid BEGIN_LINK link END_LINK and spaces.",
+      @"expected" : @"Text with valid link and spaces.",
+      @"link range" : [NSValue valueWithRange:NSMakeRange(16, 4)]
+    },
+    @{
+      @"input" : @"Text with valid BEGIN_LINKlinkEND_LINK and no spaces.",
+      @"expected" : @"Text with valid link and no spaces.",
+      @"link range" : [NSValue valueWithRange:NSMakeRange(16, 4)]
+    }
+  ];
+  for (NSDictionary* test_data : all_test_data) {
+    NSString* input_text = test_data[@"input"];
+    NSString* expected_text = test_data[@"expected"];
+    NSRange expected_range = [test_data[@"link range"] rangeValue];
+
+    EXPECT_NSEQ(expected_text, ParseStringWithLink(input_text, nullptr));
+
+    // Initialize |range| with some values that are not equal to the expected
+    // ones.
+    NSRange range = NSMakeRange(1000, 2000);
+    EXPECT_NSEQ(expected_text, ParseStringWithLink(input_text, &range));
+    EXPECT_EQ(expected_range.location, range.location);
+    EXPECT_EQ(expected_range.length, range.length);
+  }
+}
+
+}  // namespace
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index b2029dc..3b0d9992 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -39,6 +39,7 @@
         '../provider/ios_provider_chrome.gyp:ios_provider_chrome_browser',
         '../web/ios_web.gyp:ios_web',
         'injected_js',
+        'ios_chrome_common',
         'ios_chrome_resources.gyp:ios_theme_resources_gen',
       ],
       'link_settings': {
@@ -199,6 +200,26 @@
       ],
     },
     {
+      'target_name': 'ios_chrome_common',
+      'type': 'static_library',
+      'include_dirs': [
+        '../..',
+      ],
+      'dependencies': [
+        '../../base/base.gyp:base',
+      ],
+      'link_settings': {
+        'libraries': [
+          '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+        ],
+      },
+      'sources': [
+        'common/string_util.h',
+        'common/string_util.mm',
+      ]
+    },
+    {
       'target_name': 'injected_js',
       'type': 'none',
       'sources': [
diff --git a/ios/chrome/ios_chrome_tests.gyp b/ios/chrome/ios_chrome_tests.gyp
index 7cea1a3..3af8d8f 100644
--- a/ios/chrome/ios_chrome_tests.gyp
+++ b/ios/chrome/ios_chrome_tests.gyp
@@ -20,6 +20,7 @@
         '../web/ios_web.gyp:ios_web',
         '../web/ios_web.gyp:test_support_ios_web',
         'ios_chrome.gyp:ios_chrome_browser',
+        'ios_chrome.gyp:ios_chrome_common',
         'ios_chrome_test_support',
       ],
       'sources': [
@@ -32,6 +33,7 @@
         'browser/ui/commands/set_up_for_testing_command_unittest.mm',
         'browser/ui/ui_util_unittest.mm',
         'browser/ui/uikit_ui_util_unittest.mm',
+        'common/string_util_unittest.mm',
       ],
     },
     {
diff --git a/ios/chrome/test/DEPS b/ios/chrome/test/DEPS
index 9074a78..0614aac1 100644
--- a/ios/chrome/test/DEPS
+++ b/ios/chrome/test/DEPS
@@ -1,3 +1,7 @@
 include_rules = [
   "+ios/chrome/browser",
+  "+ios/public/provider/chrome",
+  "+ios/public/test",
+  "+ios/web/public",
+  "+ui",
 ]
diff --git a/jingle/glue/proxy_resolving_client_socket.cc b/jingle/glue/proxy_resolving_client_socket.cc
index 5a6e068..456e100 100644
--- a/jingle/glue/proxy_resolving_client_socket.cc
+++ b/jingle/glue/proxy_resolving_client_socket.cc
@@ -9,6 +9,7 @@
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/profiler/scoped_tracker.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -212,6 +213,10 @@
 }
 
 void ProxyResolvingClientSocket::ProcessConnectDone(int status) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 ProxyResolvingClientSocket::OnConnectDone"));
   if (status != net::OK) {
     // If the connection fails, try another proxy.
     status = ReconsiderProxyAfterError(status);
diff --git a/media/base/cdm_factory.h b/media/base/cdm_factory.h
index 2a9b42d..f9c5541b 100644
--- a/media/base/cdm_factory.h
+++ b/media/base/cdm_factory.h
@@ -17,10 +17,14 @@
 
 class MEDIA_EXPORT CdmFactory {
  public:
+  using CdmCreatedCB = base::Callback<void(scoped_ptr<MediaKeys>)>;
+
   CdmFactory();
   virtual ~CdmFactory();
 
-  virtual scoped_ptr<MediaKeys> Create(
+  // Creates a CDM for |key_system| and returns it through |cdm_created_cb|
+  // asynchronously.
+  virtual void Create(
       const std::string& key_system,
       bool allow_distinctive_identifier,
       bool allow_persistent_state,
@@ -29,7 +33,8 @@
       const SessionClosedCB& session_closed_cb,
       const LegacySessionErrorCB& legacy_session_error_cb,
       const SessionKeysChangeCB& session_keys_change_cb,
-      const SessionExpirationUpdateCB& session_expiration_update_cb) = 0;
+      const SessionExpirationUpdateCB& session_expiration_update_cb,
+      const CdmCreatedCB& cdm_created_cb) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CdmFactory);
diff --git a/media/blink/cdm_session_adapter.cc b/media/blink/cdm_session_adapter.cc
index 79f5054..8e62bcee 100644
--- a/media/blink/cdm_session_adapter.cc
+++ b/media/blink/cdm_session_adapter.cc
@@ -13,6 +13,7 @@
 #include "media/base/cdm_promise.h"
 #include "media/base/key_systems.h"
 #include "media/base/media_keys.h"
+#include "media/blink/webcontentdecryptionmodule_impl.h"
 #include "media/blink/webcontentdecryptionmodulesession_impl.h"
 #include "url/gurl.h"
 
@@ -26,33 +27,34 @@
 
 CdmSessionAdapter::~CdmSessionAdapter() {}
 
-bool CdmSessionAdapter::Initialize(CdmFactory* cdm_factory,
-                                   const std::string& key_system,
-                                   bool allow_distinctive_identifier,
-                                   bool allow_persistent_state,
-                                   const GURL& security_origin) {
-  key_system_ = key_system;
-  key_system_uma_prefix_ =
-      kMediaEME + GetKeySystemNameForUMA(key_system) + kDot;
-
+void CdmSessionAdapter::CreateCdm(
+    CdmFactory* cdm_factory,
+    const std::string& key_system,
+    bool allow_distinctive_identifier,
+    bool allow_persistent_state,
+    const GURL& security_origin,
+    blink::WebContentDecryptionModuleResult result) {
+  // Note: WebContentDecryptionModuleImpl::Create() calls this method without
+  // holding a reference to the CdmSessionAdapter. Bind OnCdmCreated() with
+  // |this| instead of |weak_this| to prevent |this| from being desctructed.
   base::WeakPtr<CdmSessionAdapter> weak_this = weak_ptr_factory_.GetWeakPtr();
-  media_keys_ = cdm_factory->Create(
+  cdm_factory->Create(
       key_system, allow_distinctive_identifier, allow_persistent_state,
       security_origin,
       base::Bind(&CdmSessionAdapter::OnSessionMessage, weak_this),
       base::Bind(&CdmSessionAdapter::OnSessionClosed, weak_this),
       base::Bind(&CdmSessionAdapter::OnLegacySessionError, weak_this),
       base::Bind(&CdmSessionAdapter::OnSessionKeysChange, weak_this),
-      base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this));
-  return media_keys_.get() != nullptr;
+      base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this),
+      base::Bind(&CdmSessionAdapter::OnCdmCreated, this, key_system, result));
 }
 
 void CdmSessionAdapter::SetServerCertificate(
     const uint8* server_certificate,
     int server_certificate_length,
     scoped_ptr<SimpleCdmPromise> promise) {
-  media_keys_->SetServerCertificate(
-      server_certificate, server_certificate_length, promise.Pass());
+  cdm_->SetServerCertificate(server_certificate, server_certificate_length,
+                             promise.Pass());
 }
 
 WebContentDecryptionModuleSessionImpl* CdmSessionAdapter::CreateSession() {
@@ -81,37 +83,35 @@
     int init_data_length,
     MediaKeys::SessionType session_type,
     scoped_ptr<NewSessionCdmPromise> promise) {
-  media_keys_->CreateSessionAndGenerateRequest(session_type, init_data_type,
-                                               init_data, init_data_length,
-                                               promise.Pass());
+  cdm_->CreateSessionAndGenerateRequest(session_type, init_data_type, init_data,
+                                        init_data_length, promise.Pass());
 }
 
 void CdmSessionAdapter::LoadSession(MediaKeys::SessionType session_type,
                                     const std::string& session_id,
                                     scoped_ptr<NewSessionCdmPromise> promise) {
-  media_keys_->LoadSession(session_type, session_id, promise.Pass());
+  cdm_->LoadSession(session_type, session_id, promise.Pass());
 }
 
 void CdmSessionAdapter::UpdateSession(const std::string& session_id,
                                       const uint8* response,
                                       int response_length,
                                       scoped_ptr<SimpleCdmPromise> promise) {
-  media_keys_->UpdateSession(session_id, response, response_length,
-                             promise.Pass());
+  cdm_->UpdateSession(session_id, response, response_length, promise.Pass());
 }
 
 void CdmSessionAdapter::CloseSession(const std::string& session_id,
                                      scoped_ptr<SimpleCdmPromise> promise) {
-  media_keys_->CloseSession(session_id, promise.Pass());
+  cdm_->CloseSession(session_id, promise.Pass());
 }
 
 void CdmSessionAdapter::RemoveSession(const std::string& session_id,
                                       scoped_ptr<SimpleCdmPromise> promise) {
-  media_keys_->RemoveSession(session_id, promise.Pass());
+  cdm_->RemoveSession(session_id, promise.Pass());
 }
 
 CdmContext* CdmSessionAdapter::GetCdmContext() {
-  return media_keys_->GetCdmContext();
+  return cdm_->GetCdmContext();
 }
 
 const std::string& CdmSessionAdapter::GetKeySystem() const {
@@ -122,6 +122,27 @@
   return key_system_uma_prefix_;
 }
 
+void CdmSessionAdapter::OnCdmCreated(
+    const std::string& key_system,
+    blink::WebContentDecryptionModuleResult result,
+    scoped_ptr<MediaKeys> cdm) {
+  DVLOG(2) << __FUNCTION__;
+  if (!cdm) {
+    result.completeWithError(
+        blink::WebContentDecryptionModuleExceptionNotSupportedError, 0,
+        "Failed to create the CDM instance.");
+    return;
+  }
+
+  key_system_ = key_system;
+  key_system_uma_prefix_ =
+      kMediaEME + GetKeySystemNameForUMA(key_system) + kDot;
+  cdm_ = cdm.Pass();
+
+  result.completeWithContentDecryptionModule(
+      new WebContentDecryptionModuleImpl(this));
+}
+
 void CdmSessionAdapter::OnSessionMessage(
     const std::string& session_id,
     MediaKeys::MessageType message_type,
diff --git a/media/blink/cdm_session_adapter.h b/media/blink/cdm_session_adapter.h
index e35c4cb..5ed7550e 100644
--- a/media/blink/cdm_session_adapter.h
+++ b/media/blink/cdm_session_adapter.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "media/base/media_keys.h"
+#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h"
 
 class GURL;
@@ -30,12 +31,14 @@
  public:
   CdmSessionAdapter();
 
-  // Returns true on success.
-  bool Initialize(CdmFactory* cdm_factory,
-                  const std::string& key_system,
-                  bool allow_distinctive_identifier,
-                  bool allow_persistent_state,
-                  const GURL& security_origin);
+  // Creates the CDM for |key_system| using |cdm_factory| and returns the result
+  // via |result|.
+  void CreateCdm(CdmFactory* cdm_factory,
+                 const std::string& key_system,
+                 bool allow_distinctive_identifier,
+                 bool allow_persistent_state,
+                 const GURL& security_origin,
+                 blink::WebContentDecryptionModuleResult result);
 
   // Provides a server certificate to be used to encrypt messages to the
   // license server.
@@ -99,12 +102,19 @@
 
  private:
   friend class base::RefCounted<CdmSessionAdapter>;
+
+  // Session ID to WebContentDecryptionModuleSessionImpl mapping.
   typedef base::hash_map<std::string,
                          base::WeakPtr<WebContentDecryptionModuleSessionImpl> >
       SessionMap;
 
   ~CdmSessionAdapter();
 
+  // Callback for CreateCdm().
+  void OnCdmCreated(const std::string& key_system,
+                    blink::WebContentDecryptionModuleResult result,
+                    scoped_ptr<MediaKeys> cdm);
+
   // Callbacks for firing session events.
   void OnSessionMessage(const std::string& session_id,
                         MediaKeys::MessageType message_type,
@@ -125,7 +135,7 @@
   WebContentDecryptionModuleSessionImpl* GetSession(
       const std::string& session_id);
 
-  scoped_ptr<MediaKeys> media_keys_;
+  scoped_ptr<MediaKeys> cdm_;
 
   SessionMap sessions_;
 
diff --git a/media/blink/encrypted_media_player_support.cc b/media/blink/encrypted_media_player_support.cc
index a84e24c..cc11a2e 100644
--- a/media/blink/encrypted_media_player_support.cc
+++ b/media/blink/encrypted_media_player_support.cc
@@ -116,12 +116,12 @@
     CdmFactory* cdm_factory,
     blink::WebMediaPlayerClient* client,
     MediaPermission* media_permission,
-    const SetCdmContextCB& set_cdm_context_cb)
+    const CdmContextReadyCB& cdm_context_ready_cb)
     : cdm_factory_(cdm_factory),
       client_(client),
       media_permission_(media_permission),
       init_data_type_(EmeInitDataType::UNKNOWN),
-      set_cdm_context_cb_(set_cdm_context_cb) {
+      cdm_context_ready_cb_(cdm_context_ready_cb) {
 }
 
 EncryptedMediaPlayerSupport::~EncryptedMediaPlayerSupport() {
@@ -155,43 +155,32 @@
   if (!PrefixedIsSupportedConcreteKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
-  // We do not support run-time switching between key systems for now.
-  if (current_key_system_.empty()) {
-    if (!proxy_decryptor_) {
-      proxy_decryptor_.reset(new ProxyDecryptor(
-          media_permission_,
-          BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded),
-          BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError),
-          BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage)));
-    }
+  if (!proxy_decryptor_) {
+    DCHECK(current_key_system_.empty());
+    DCHECK(!cdm_context_ready_cb_.is_null());
+    proxy_decryptor_.reset(new ProxyDecryptor(
+        media_permission_,
+        BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded),
+        BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError),
+        BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage)));
 
     GURL security_origin(frame->document().securityOrigin().toString());
-
-    if (!proxy_decryptor_->InitializeCDM(cdm_factory_, key_system,
-                                         security_origin)) {
-      return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
-    }
-
-    if (proxy_decryptor_ && !set_cdm_context_cb_.is_null()) {
-      base::ResetAndReturn(&set_cdm_context_cb_)
-          .Run(proxy_decryptor_->GetCdmContext(),
-               base::Bind(&IgnoreCdmAttached));
-    }
-
+    proxy_decryptor_->CreateCdm(cdm_factory_, key_system, security_origin,
+                                cdm_context_ready_cb_);
     current_key_system_ = key_system;
-  } else if (key_system != current_key_system_) {
-    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
   }
 
+  // We do not support run-time switching between key systems for now.
+  DCHECK(!current_key_system_.empty());
+  if (key_system != current_key_system_)
+    return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
+
   EmeInitDataType init_data_type = init_data_type_;
   if (init_data_type == EmeInitDataType::UNKNOWN)
     init_data_type = GuessInitDataType(init_data, init_data_length);
 
-  if (!proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
-                                            init_data_length)) {
-    current_key_system_.clear();
-    return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
-  }
+  proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
+                                       init_data_length);
 
   return WebMediaPlayer::MediaKeyExceptionNoError;
 }
diff --git a/media/blink/encrypted_media_player_support.h b/media/blink/encrypted_media_player_support.h
index 30d87086..d3fd454b 100644
--- a/media/blink/encrypted_media_player_support.h
+++ b/media/blink/encrypted_media_player_support.h
@@ -37,13 +37,13 @@
 class EncryptedMediaPlayerSupport
     : public base::SupportsWeakPtr<EncryptedMediaPlayerSupport> {
  public:
-  typedef base::Callback<void(CdmContext*, const CdmAttachedCB&)>
-      SetCdmContextCB;
+  using CdmContextReadyCB = ProxyDecryptor::CdmContextReadyCB;
 
+  // |cdm_context_ready_cb| is called when the CDM instance creation completes.
   EncryptedMediaPlayerSupport(CdmFactory* cdm_factory,
                               blink::WebMediaPlayerClient* client,
                               MediaPermission* media_permission,
-                              const SetCdmContextCB& set_cdm_context_cb);
+                              const CdmContextReadyCB& cdm_context_ready_cb);
   ~EncryptedMediaPlayerSupport();
 
   blink::WebMediaPlayer::MediaKeyException GenerateKeyRequest(
@@ -109,7 +109,7 @@
   // init data type.
   EmeInitDataType init_data_type_;
 
-  SetCdmContextCB set_cdm_context_cb_;
+  CdmContextReadyCB cdm_context_ready_cb_;
 
   // Manages decryption keys and decrypts encrypted frames.
   scoped_ptr<ProxyDecryptor> proxy_decryptor_;
diff --git a/media/blink/webcontentdecryptionmodule_impl.cc b/media/blink/webcontentdecryptionmodule_impl.cc
index 9d60220..7bdd9c9e 100644
--- a/media/blink/webcontentdecryptionmodule_impl.cc
+++ b/media/blink/webcontentdecryptionmodule_impl.cc
@@ -62,21 +62,15 @@
   }
 
   GURL security_origin_as_gurl(security_origin.toString());
+
+  // CdmSessionAdapter::CreateCdm() will keep a reference to |adapter|. Then
+  // if WebContentDecryptionModuleImpl is successfully created (returned in
+  // |result|), it will keep a reference to |adapter|. Otherwise, |adapter| will
+  // be destructed.
   scoped_refptr<CdmSessionAdapter> adapter(new CdmSessionAdapter());
-
-  // TODO(jrummell): Pass WebContentDecryptionModuleResult (or similar) to
-  // Initialize() so that more specific errors can be reported.
-  if (!adapter->Initialize(cdm_factory, key_system_ascii,
-                           allow_distinctive_identifier,
-                           allow_persistent_state, security_origin_as_gurl)) {
-    result.completeWithError(
-        blink::WebContentDecryptionModuleExceptionNotSupportedError, 0,
-        "Failed to initialize CDM.");
-    return;
-  }
-
-  result.completeWithContentDecryptionModule(
-      new WebContentDecryptionModuleImpl(adapter));
+  adapter->CreateCdm(cdm_factory, key_system_ascii,
+                     allow_distinctive_identifier, allow_persistent_state,
+                     security_origin_as_gurl, result);
 }
 
 WebContentDecryptionModuleImpl::WebContentDecryptionModuleImpl(
diff --git a/media/blink/webcontentdecryptionmodule_impl.h b/media/blink/webcontentdecryptionmodule_impl.h
index 8b38cc6b..aa7fa16 100644
--- a/media/blink/webcontentdecryptionmodule_impl.h
+++ b/media/blink/webcontentdecryptionmodule_impl.h
@@ -52,6 +52,8 @@
   CdmContext* GetCdmContext();
 
  private:
+  friend CdmSessionAdapter;
+
   // Takes reference to |adapter|.
   WebContentDecryptionModuleImpl(scoped_refptr<CdmSessionAdapter> adapter);
 
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index fe30c5ef..ab62772 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -137,11 +137,12 @@
       compositor_(new VideoFrameCompositor(
           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged),
           BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))),
-      encrypted_media_support_(
-          cdm_factory,
-          client,
-          params.media_permission(),
-          base::Bind(&WebMediaPlayerImpl::SetCdm, AsWeakPtr())),
+      encrypted_media_support_(cdm_factory,
+                               client,
+                               params.media_permission(),
+                               base::Bind(&WebMediaPlayerImpl::SetCdm,
+                                          AsWeakPtr(),
+                                          base::Bind(&IgnoreCdmAttached))),
       renderer_factory_(renderer_factory.Pass()) {
   // Threaded compositing isn't enabled universally yet.
   if (!compositor_task_runner_.get())
@@ -151,9 +152,9 @@
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
 
   if (params.initial_cdm()) {
-    SetCdm(
-        ToWebContentDecryptionModuleImpl(params.initial_cdm())->GetCdmContext(),
-        base::Bind(&IgnoreCdmAttached));
+    SetCdm(base::Bind(&IgnoreCdmAttached),
+           ToWebContentDecryptionModuleImpl(params.initial_cdm())
+               ->GetCdmContext());
   }
 
   // TODO(xhwang): When we use an external Renderer, many methods won't work,
@@ -660,8 +661,8 @@
     return;
   }
 
-  SetCdm(ToWebContentDecryptionModuleImpl(cdm)->GetCdmContext(),
-         BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnCdmAttached, result));
+  SetCdm(BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnCdmAttached, result),
+         ToWebContentDecryptionModuleImpl(cdm)->GetCdmContext());
 }
 
 void WebMediaPlayerImpl::OnEncryptedMediaInitData(
@@ -695,8 +696,8 @@
   client_->didResumePlaybackBlockedForKey();
 }
 
-void WebMediaPlayerImpl::SetCdm(CdmContext* cdm_context,
-                                const CdmAttachedCB& cdm_attached_cb) {
+void WebMediaPlayerImpl::SetCdm(const CdmAttachedCB& cdm_attached_cb,
+                                CdmContext* cdm_context) {
   pipeline_.SetCdm(cdm_context, cdm_attached_cb);
 }
 
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 80bea8f3c..427357b1 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -224,7 +224,9 @@
   // is not available.
   void OnWaitingForDecryptionKey();
 
-  void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb);
+  // Sets |cdm_context| on the pipeline and fires |cdm_attached_cb| when done.
+  // Parameter order is reversed for easy binding.
+  void SetCdm(const CdmAttachedCB& cdm_attached_cb, CdmContext* cdm_context);
 
   // Called when a CDM has been attached to the |pipeline_|.
   void OnCdmAttached(blink::WebContentDecryptionModuleResult result,
diff --git a/media/cdm/default_cdm_factory.cc b/media/cdm/default_cdm_factory.cc
index a87760c12..202f3cdc 100644
--- a/media/cdm/default_cdm_factory.cc
+++ b/media/cdm/default_cdm_factory.cc
@@ -4,6 +4,10 @@
 
 #include "media/cdm/default_cdm_factory.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "media/base/key_systems.h"
 #include "media/cdm/aes_decryptor.h"
 #include "url/gurl.h"
@@ -16,7 +20,7 @@
 DefaultCdmFactory::~DefaultCdmFactory() {
 }
 
-scoped_ptr<MediaKeys> DefaultCdmFactory::Create(
+void DefaultCdmFactory::Create(
     const std::string& key_system,
     bool allow_distinctive_identifier,
     bool allow_persistent_state,
@@ -25,17 +29,19 @@
     const SessionClosedCB& session_closed_cb,
     const LegacySessionErrorCB& legacy_session_error_cb,
     const SessionKeysChangeCB& session_keys_change_cb,
-    const SessionExpirationUpdateCB& session_expiration_update_cb) {
-  if (!security_origin.is_valid())
-    return nullptr;
-
-  if (CanUseAesDecryptor(key_system)) {
-    return make_scoped_ptr(new AesDecryptor(security_origin, session_message_cb,
-                                            session_closed_cb,
-                                            session_keys_change_cb));
+    const SessionExpirationUpdateCB& session_expiration_update_cb,
+    const CdmCreatedCB& cdm_created_cb) {
+  if (!security_origin.is_valid() || !CanUseAesDecryptor(key_system)) {
+    base::MessageLoopProxy::current()->PostTask(
+        FROM_HERE, base::Bind(cdm_created_cb, nullptr));
+    return;
   }
 
-  return nullptr;
+  scoped_ptr<MediaKeys> cdm(
+      new AesDecryptor(security_origin, session_message_cb, session_closed_cb,
+                       session_keys_change_cb));
+  base::MessageLoopProxy::current()->PostTask(
+      FROM_HERE, base::Bind(cdm_created_cb, base::Passed(&cdm)));
 }
 
 }  // namespace media
diff --git a/media/cdm/default_cdm_factory.h b/media/cdm/default_cdm_factory.h
index 72e0e8d..7060c7c4 100644
--- a/media/cdm/default_cdm_factory.h
+++ b/media/cdm/default_cdm_factory.h
@@ -16,16 +16,16 @@
   ~DefaultCdmFactory() final;
 
   // CdmFactory implementation.
-  scoped_ptr<MediaKeys> Create(
-      const std::string& key_system,
-      bool allow_distinctive_identifier,
-      bool allow_persistent_state,
-      const GURL& security_origin,
-      const SessionMessageCB& session_message_cb,
-      const SessionClosedCB& session_closed_cb,
-      const LegacySessionErrorCB& legacy_session_error_cb,
-      const SessionKeysChangeCB& session_keys_change_cb,
-      const SessionExpirationUpdateCB& session_expiration_update_cb) final;
+  void Create(const std::string& key_system,
+              bool allow_distinctive_identifier,
+              bool allow_persistent_state,
+              const GURL& security_origin,
+              const SessionMessageCB& session_message_cb,
+              const SessionClosedCB& session_closed_cb,
+              const LegacySessionErrorCB& legacy_session_error_cb,
+              const SessionKeysChangeCB& session_keys_change_cb,
+              const SessionExpirationUpdateCB& session_expiration_update_cb,
+              const CdmCreatedCB& cdm_created_cb) final;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DefaultCdmFactory);
diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc
index 21df8377..43533f0 100644
--- a/media/cdm/json_web_key.cc
+++ b/media/cdm/json_web_key.cc
@@ -19,8 +19,6 @@
 const char kKeysTag[] = "keys";
 const char kKeyTypeTag[] = "kty";
 const char kKeyTypeOct[] = "oct";  // Octet sequence.
-const char kAlgTag[] = "alg";
-const char kAlgA128KW[] = "A128KW";  // AES key wrap using a 128-bit key.
 const char kKeyTag[] = "k";
 const char kKeyIdTag[] = "kid";
 const char kKeyIdsTag[] = "kids";
@@ -113,7 +111,6 @@
   // Create the JWK, and wrap it into a JWK Set.
   scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
   jwk->SetString(kKeyTypeTag, kKeyTypeOct);
-  jwk->SetString(kAlgTag, kAlgA128KW);
   jwk->SetString(kKeyTag, key_base64);
   jwk->SetString(kKeyIdTag, key_id_base64);
   scoped_ptr<base::ListValue> list(new base::ListValue());
@@ -138,12 +135,6 @@
     return false;
   }
 
-  std::string alg;
-  if (!jwk.GetString(kAlgTag, &alg) || alg != kAlgA128KW) {
-    DVLOG(1) << "Missing or invalid '" << kAlgTag << "': " << alg;
-    return false;
-  }
-
   // Get the key id and actual key parameters.
   std::string encoded_key_id;
   std::string encoded_key;
diff --git a/media/cdm/json_web_key_unittest.cc b/media/cdm/json_web_key_unittest.cc
index 0cb251c..ecd9d2a 100644
--- a/media/cdm/json_web_key_unittest.cc
+++ b/media/cdm/json_web_key_unittest.cc
@@ -79,24 +79,17 @@
   const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                           0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
 
+  EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
+            GenerateJWKSet(data1, arraysize(data1), data1, arraysize(data1)));
   EXPECT_EQ(
-      "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":"
-      "\"oct\"}]}",
-      GenerateJWKSet(data1, arraysize(data1), data1, arraysize(data1)));
-  EXPECT_EQ(
-      "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\","
-      "\"kty\":\"oct\"}]}",
+      "{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
       GenerateJWKSet(data2, arraysize(data2), data2, arraysize(data2)));
+  EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
+            GenerateJWKSet(data1, arraysize(data1), data2, arraysize(data2)));
+  EXPECT_EQ("{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
+            GenerateJWKSet(data2, arraysize(data2), data1, arraysize(data1)));
   EXPECT_EQ(
-      "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":"
-      "\"oct\"}]}",
-      GenerateJWKSet(data1, arraysize(data1), data2, arraysize(data2)));
-  EXPECT_EQ(
-      "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":"
-      "\"oct\"}]}",
-      GenerateJWKSet(data2, arraysize(data2), data1, arraysize(data1)));
-  EXPECT_EQ(
-      "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":"
+      "{\"keys\":[{\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":"
       "\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kty\":\"oct\"}]}",
       GenerateJWKSet(data3, arraysize(data3), data3, arraysize(data3)));
 }
@@ -111,7 +104,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
       "    }"
@@ -125,13 +117,11 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
       "    },"
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
       "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
       "    }"
@@ -141,7 +131,7 @@
 
   // Try a key with no spaces and some \n plus additional fields.
   const std::string kJwksNoSpaces =
-      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
+      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\","
       "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
       "\",\"foo\":\"bar\"}]}\n\n";
   ExtractJWKKeysAndExpect(kJwksNoSpaces, true, 1);
@@ -152,13 +142,11 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
       "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
       "    },"
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
       "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
       "    }"
@@ -172,7 +160,6 @@
   const std::string kJwkSimple =
       "{"
       "  \"kty\": \"oct\","
-      "  \"alg\": \"A128KW\","
       "  \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "  \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
       "}";
@@ -184,7 +171,7 @@
 
   // Try some non-ASCII characters in an otherwise valid JWK.
   const std::string kJwksInvalidCharacters =
-      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
+      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\","
       "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"\xff\xfe\xfd"
       "\",\"foo\":\"bar\"}]}\n\n";
   ExtractJWKKeysAndExpect(kJwksInvalidCharacters, false, 0);
@@ -212,7 +199,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAw\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\""
       "    }"
@@ -226,7 +212,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAw==\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -240,7 +225,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"!@#$%^&*()\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -254,7 +238,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -270,7 +253,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"oct\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -284,7 +266,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -298,7 +279,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"OCT\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -312,7 +292,6 @@
       "  \"keys\": ["
       "    {"
       "      \"kty\": \"RSA\","
-      "      \"alg\": \"A128KW\","
       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
       "    }"
@@ -322,6 +301,7 @@
 }
 
 TEST_F(JSONWebKeyTest, Alg) {
+  // 'alg' is ignored, so verify that anything is allowed.
   // Valid alg.
   const std::string kJwksWithValidAlg =
       "{"
@@ -348,7 +328,7 @@
       "    }"
       "  ]"
       "}";
-  ExtractJWKKeysAndExpect(kJwksWithEmptyAlg, false, 0);
+  ExtractJWKKeysAndExpect(kJwksWithEmptyAlg, true, 1);
 
   // Alg is case sensitive.
   const std::string kJwksWithLowercaseAlg =
@@ -362,7 +342,7 @@
       "    }"
       "  ]"
       "}";
-  ExtractJWKKeysAndExpect(kJwksWithLowercaseAlg, false, 0);
+  ExtractJWKKeysAndExpect(kJwksWithLowercaseAlg, true, 1);
 
   // Wrong alg.
   const std::string kJwksWithWrongAlg =
@@ -376,37 +356,31 @@
       "    }"
       "  ]"
       "}";
-  ExtractJWKKeysAndExpect(kJwksWithWrongAlg, false, 0);
+  ExtractJWKKeysAndExpect(kJwksWithWrongAlg, true, 1);
 }
 
 TEST_F(JSONWebKeyTest, SessionType) {
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
-      true, MediaKeys::TEMPORARY_SESSION);
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}", true,
+      MediaKeys::TEMPORARY_SESSION);
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
       "\"temporary\"}",
       true, MediaKeys::TEMPORARY_SESSION);
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
       "\"persistent-license\"}",
       true, MediaKeys::PERSISTENT_LICENSE_SESSION);
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
       "\"persistent-release-message\"}",
       true, MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION);
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
       "\"unknown\"}",
       false, MediaKeys::TEMPORARY_SESSION);
   ExtractSessionTypeAndExpect(
-      "{\"keys\":[{\"alg\": "
-      "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":3}",
+      "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":3}",
       false, MediaKeys::TEMPORARY_SESSION);
 }
 
diff --git a/media/cdm/proxy_decryptor.cc b/media/cdm/proxy_decryptor.cc
index 944b6140..9260e14 100644
--- a/media/cdm/proxy_decryptor.cc
+++ b/media/cdm/proxy_decryptor.cc
@@ -25,11 +25,22 @@
 // EME API.
 const int kSessionClosedSystemCode = 29127;
 
+ProxyDecryptor::PendingGenerateKeyRequestData::PendingGenerateKeyRequestData(
+    EmeInitDataType init_data_type,
+    const std::vector<uint8>& init_data)
+    : init_data_type(init_data_type), init_data(init_data) {
+}
+
+ProxyDecryptor::PendingGenerateKeyRequestData::
+    ~PendingGenerateKeyRequestData() {
+}
+
 ProxyDecryptor::ProxyDecryptor(MediaPermission* media_permission,
                                const KeyAddedCB& key_added_cb,
                                const KeyErrorCB& key_error_cb,
                                const KeyMessageCB& key_message_cb)
-    : media_permission_(media_permission),
+    : is_creating_cdm_(false),
+      media_permission_(media_permission),
       key_added_cb_(key_added_cb),
       key_error_cb_(key_error_cb),
       key_message_cb_(key_message_cb),
@@ -46,33 +57,77 @@
   media_keys_.reset();
 }
 
-CdmContext* ProxyDecryptor::GetCdmContext() {
-  return media_keys_ ? media_keys_->GetCdmContext() : nullptr;
+void ProxyDecryptor::CreateCdm(CdmFactory* cdm_factory,
+                               const std::string& key_system,
+                               const GURL& security_origin,
+                               const CdmContextReadyCB& cdm_context_ready_cb) {
+  DVLOG(1) << __FUNCTION__ << ": key_system = " << key_system;
+  DCHECK(!is_creating_cdm_);
+  DCHECK(!media_keys_);
+
+  // TODO(sandersd): Trigger permissions check here and use it to determine
+  // distinctive identifier support, instead of always requiring the
+  // permission. http://crbug.com/455271
+  bool allow_distinctive_identifier = true;
+  bool allow_persistent_state = true;
+
+  is_creating_cdm_ = true;
+
+  base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
+  cdm_factory->Create(
+      key_system, allow_distinctive_identifier, allow_persistent_state,
+      security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
+      base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
+      base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this),
+      base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
+      base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this),
+      base::Bind(&ProxyDecryptor::OnCdmCreated, weak_this, key_system,
+                 security_origin, cdm_context_ready_cb));
 }
 
-bool ProxyDecryptor::InitializeCDM(CdmFactory* cdm_factory,
-                                   const std::string& key_system,
-                                   const GURL& security_origin) {
-  DVLOG(1) << "InitializeCDM: key_system = " << key_system;
+void ProxyDecryptor::OnCdmCreated(const std::string& key_system,
+                                  const GURL& security_origin,
+                                  const CdmContextReadyCB& cdm_context_ready_cb,
+                                  scoped_ptr<MediaKeys> cdm) {
+  is_creating_cdm_ = false;
 
-  DCHECK(!media_keys_);
-  media_keys_ = CreateMediaKeys(cdm_factory, key_system, security_origin);
-  if (!media_keys_)
-    return false;
+  if (!cdm) {
+    cdm_context_ready_cb.Run(nullptr);
+    return;
+  }
 
   key_system_ = key_system;
   security_origin_ = security_origin;
+  is_clear_key_ = IsClearKey(key_system) || IsExternalClearKey(key_system);
+  media_keys_ = cdm.Pass();
 
-  is_clear_key_ =
-      IsClearKey(key_system) || IsExternalClearKey(key_system);
-  return true;
+  cdm_context_ready_cb.Run(media_keys_->GetCdmContext());
+
+  for (const auto& request : pending_requests_)
+    GenerateKeyRequestInternal(request->init_data_type, request->init_data);
+
+  pending_requests_.clear();
+}
+
+void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type,
+                                        const uint8* init_data,
+                                        int init_data_length) {
+  std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
+
+  if (is_creating_cdm_) {
+    pending_requests_.push_back(
+        new PendingGenerateKeyRequestData(init_data_type, init_data_vector));
+    return;
+  }
+
+  GenerateKeyRequestInternal(init_data_type, init_data_vector);
 }
 
 // Returns true if |data| is prefixed with |header| and has data after the
 // |header|.
-bool HasHeader(const uint8* data, int data_length, const std::string& header) {
-  return static_cast<size_t>(data_length) > header.size() &&
-         std::equal(data, data + header.size(), header.begin());
+bool HasHeader(const std::vector<uint8>& data, const std::string& header) {
+  return data.size() > header.size() &&
+         std::equal(header.begin(), header.end(), data.begin());
 }
 
 // Removes the first |length| items from |data|.
@@ -80,23 +135,30 @@
   data.erase(data.begin(), data.begin() + length);
 }
 
-bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type,
-                                        const uint8* init_data,
-                                        int init_data_length) {
-  DVLOG(1) << "GenerateKeyRequest()";
+void ProxyDecryptor::GenerateKeyRequestInternal(
+    EmeInitDataType init_data_type,
+    const std::vector<uint8>& init_data) {
+  DVLOG(1) << __FUNCTION__;
+  DCHECK(!is_creating_cdm_);
+
+  if (!media_keys_) {
+    OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0,
+                         "CDM creation failed.");
+    return;
+  }
+
   const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
   const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
 
   SessionCreationType session_creation_type = TemporarySession;
-  std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
-  if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
+  std::vector<uint8> stripped_init_data = init_data;
+  if (HasHeader(init_data, kPrefixedApiLoadSessionHeader)) {
     session_creation_type = LoadSession;
-    StripHeader(init_data_vector, strlen(kPrefixedApiLoadSessionHeader));
-  } else if (HasHeader(init_data,
-                       init_data_length,
-                       kPrefixedApiPersistentSessionHeader)) {
+    StripHeader(stripped_init_data, strlen(kPrefixedApiLoadSessionHeader));
+  } else if (HasHeader(init_data, kPrefixedApiPersistentSessionHeader)) {
     session_creation_type = PersistentSession;
-    StripHeader(init_data_vector, strlen(kPrefixedApiPersistentSessionHeader));
+    StripHeader(stripped_init_data,
+                strlen(kPrefixedApiPersistentSessionHeader));
   }
 
   scoped_ptr<NewSessionCdmPromise> promise(new CdmCallbackPromise<std::string>(
@@ -110,10 +172,10 @@
     media_keys_->LoadSession(
         MediaKeys::PERSISTENT_LICENSE_SESSION,
         std::string(
-            reinterpret_cast<const char*>(vector_as_array(&init_data_vector)),
-            init_data_vector.size()),
+            reinterpret_cast<const char*>(vector_as_array(&stripped_init_data)),
+            stripped_init_data.size()),
         promise.Pass());
-    return true;
+    return;
   }
 
   MediaKeys::SessionType session_type =
@@ -125,9 +187,9 @@
   // external clear key.
   DCHECK(!key_system_.empty());
   if (CanUseAesDecryptor(key_system_) || IsExternalClearKey(key_system_)) {
-    OnPermissionStatus(session_type, init_data_type, init_data_vector,
+    OnPermissionStatus(session_type, init_data_type, stripped_init_data,
                        promise.Pass(), true /* granted */);
-    return true;
+    return;
   }
 
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
@@ -135,13 +197,11 @@
       MediaPermission::PROTECTED_MEDIA_IDENTIFIER, security_origin_,
       base::Bind(&ProxyDecryptor::OnPermissionStatus,
                  weak_ptr_factory_.GetWeakPtr(), session_type, init_data_type,
-                 init_data_vector, base::Passed(&promise)));
+                 stripped_init_data, base::Passed(&promise)));
 #else
-  OnPermissionStatus(session_type, init_data_type, init_data_vector,
+  OnPermissionStatus(session_type, init_data_type, stripped_init_data,
                      promise.Pass(), true /* granted */);
 #endif
-
-  return true;
 }
 
 void ProxyDecryptor::OnPermissionStatus(
@@ -168,6 +228,12 @@
                             const std::string& session_id) {
   DVLOG(1) << "AddKey()";
 
+  if (!media_keys_) {
+    OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0,
+                         "CDM is not available.");
+    return;
+  }
+
   // In the prefixed API, the session parameter provided to addKey() is
   // optional, so use the single existing session if it exists.
   std::string new_session_id(session_id);
@@ -216,6 +282,12 @@
 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
   DVLOG(1) << "CancelKeyRequest()";
 
+  if (!media_keys_) {
+    OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0,
+                         "CDM is not available.");
+    return;
+  }
+
   scoped_ptr<SimpleCdmPromise> promise(new CdmCallbackPromise<>(
       base::Bind(&ProxyDecryptor::OnSessionClosed,
                  weak_ptr_factory_.GetWeakPtr(), session_id),
@@ -224,25 +296,6 @@
   media_keys_->RemoveSession(session_id, promise.Pass());
 }
 
-scoped_ptr<MediaKeys> ProxyDecryptor::CreateMediaKeys(
-    CdmFactory* cdm_factory,
-    const std::string& key_system,
-    const GURL& security_origin) {
-  // TODO(sandersd): Trigger permissions check here and use it to determine
-  // distinctive identifier support, instead of always requiring the
-  // permission. http://crbug.com/455271
-  bool allow_distinctive_identifier = true;
-  bool allow_persistent_state = true;
-  base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
-  return cdm_factory->Create(
-      key_system, allow_distinctive_identifier, allow_persistent_state,
-      security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
-      base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
-      base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this),
-      base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
-      base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this));
-}
-
 void ProxyDecryptor::OnSessionMessage(const std::string& session_id,
                                       MediaKeys::MessageType message_type,
                                       const std::vector<uint8>& message,
diff --git a/media/cdm/proxy_decryptor.h b/media/cdm/proxy_decryptor.h
index ff611ad..9a17a98c 100644
--- a/media/cdm/proxy_decryptor.h
+++ b/media/cdm/proxy_decryptor.h
@@ -11,7 +11,9 @@
 #include "base/basictypes.h"
 #include "base/containers/hash_tables.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
+#include "media/base/cdm_context.h"
 #include "media/base/decryptor.h"
 #include "media/base/eme_constants.h"
 #include "media/base/media_export.h"
@@ -32,6 +34,10 @@
 // TODO(xhwang): The ProxyDecryptor is not a Decryptor. Find a better name!
 class MEDIA_EXPORT ProxyDecryptor {
  public:
+  // Callback to provide a CdmContext when the CDM creation is finished.
+  // If CDM creation failed, |cdm_context| will be null.
+  typedef base::Callback<void(CdmContext* cdm_context)> CdmContextReadyCB;
+
   // These are similar to the callbacks in media_keys.h, but pass back the
   // session ID rather than the internal session ID.
   typedef base::Callback<void(const std::string& session_id)> KeyAddedCB;
@@ -48,16 +54,16 @@
                  const KeyMessageCB& key_message_cb);
   virtual ~ProxyDecryptor();
 
-  // Returns the CdmContext associated with this object.
-  CdmContext* GetCdmContext();
+  // Creates the CDM and fires |cdm_created_cb|. This method should only be
+  // called once. If CDM creation failed, all following GenerateKeyRequest,
+  // AddKey and CancelKeyRequest calls will result in a KeyError.
+  void CreateCdm(CdmFactory* cdm_factory,
+                 const std::string& key_system,
+                 const GURL& security_origin,
+                 const CdmContextReadyCB& cdm_context_ready_cb);
 
-  // Only call this once.
-  bool InitializeCDM(CdmFactory* cdm_factory,
-                     const std::string& key_system,
-                     const GURL& security_origin);
-
-  // May only be called after InitializeCDM() succeeds.
-  bool GenerateKeyRequest(EmeInitDataType init_data_type,
+  // May only be called after CreateCDM().
+  void GenerateKeyRequest(EmeInitDataType init_data_type,
                           const uint8* init_data,
                           int init_data_length);
   void AddKey(const uint8* key, int key_length,
@@ -66,11 +72,14 @@
   void CancelKeyRequest(const std::string& session_id);
 
  private:
-  // Helper function to create MediaKeys to handle the given |key_system|.
-  scoped_ptr<MediaKeys> CreateMediaKeys(
-      CdmFactory* cdm_factory,
-      const std::string& key_system,
-      const GURL& security_origin);
+  // Callback for CreateCdm().
+  void OnCdmCreated(const std::string& key_system,
+                    const GURL& security_origin,
+                    const CdmContextReadyCB& cdm_context_ready_cb,
+                    scoped_ptr<MediaKeys> cdm);
+
+  void GenerateKeyRequestInternal(EmeInitDataType init_data_type,
+                                  const std::vector<uint8>& init_data);
 
   // Callbacks for firing session events.
   void OnSessionMessage(const std::string& session_id,
@@ -106,6 +115,17 @@
   void SetSessionId(SessionCreationType session_type,
                     const std::string& session_id);
 
+  struct PendingGenerateKeyRequestData {
+    PendingGenerateKeyRequestData(EmeInitDataType init_data_type,
+                                  const std::vector<uint8>& init_data);
+    ~PendingGenerateKeyRequestData();
+
+    const EmeInitDataType init_data_type;
+    const std::vector<uint8> init_data;
+  };
+
+  bool is_creating_cdm_;
+
   // The real MediaKeys that manages key operations for the ProxyDecryptor.
   scoped_ptr<MediaKeys> media_keys_;
 
@@ -124,6 +144,8 @@
 
   bool is_clear_key_;
 
+  ScopedVector<PendingGenerateKeyRequestData> pending_requests_;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<ProxyDecryptor> weak_ptr_factory_;
 
diff --git a/mojo/application/BUILD.gn b/mojo/application/BUILD.gn
index e6b50dc..22c91af1 100644
--- a/mojo/application/BUILD.gn
+++ b/mojo/application/BUILD.gn
@@ -38,14 +38,18 @@
 source_set("test_support") {
   testonly = true
   sources = [
+    "application_test_base_chromium.cc",
+    "application_test_base_chromium.h",
     "application_test_main_chromium.cc",
   ]
 
-  public_deps = [
-    "//third_party/mojo/src/mojo/public/cpp/application:test_support",
-  ]
   deps = [
     "//base",
     "//base/test:test_support",
+    "//third_party/mojo/src/mojo/public/cpp/application:application",
+    "//third_party/mojo/src/mojo/public/cpp/bindings",
+    "//third_party/mojo/src/mojo/public/cpp/environment",
+    "//third_party/mojo/src/mojo/public/cpp/system",
+    "//testing/gtest",
   ]
 }
diff --git a/mojo/application/application_runner_chromium.cc b/mojo/application/application_runner_chromium.cc
index 069ff90..e5f21caa 100644
--- a/mojo/application/application_runner_chromium.cc
+++ b/mojo/application/application_runner_chromium.cc
@@ -13,6 +13,18 @@
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/application_impl.h"
 
+int g_argc;
+const char* const* g_argv;
+#if !defined(OS_WIN)
+extern "C" {
+__attribute__((visibility("default"))) void InitCommandLineArgs(
+    int argc, const char* const* argv) {
+  g_argc = argc;
+  g_argv = argv;
+}
+}
+#endif
+
 namespace mojo {
 
 // static
@@ -29,6 +41,10 @@
 
 ApplicationRunnerChromium::~ApplicationRunnerChromium() {}
 
+void ApplicationRunnerChromium::InitBaseCommandLine() {
+  base::CommandLine::Init(g_argc, g_argv);
+}
+
 void ApplicationRunnerChromium::set_message_loop_type(
     base::MessageLoop::Type type) {
   DCHECK_NE(base::MessageLoop::TYPE_CUSTOM, type);
@@ -42,7 +58,7 @@
   DCHECK(!has_run_);
   has_run_ = true;
 
-  base::CommandLine::Init(0, NULL);
+  InitBaseCommandLine();
   base::AtExitManager at_exit;
 
 #ifndef NDEBUG
diff --git a/mojo/application/application_runner_chromium.h b/mojo/application/application_runner_chromium.h
index cb586850..2d6d910 100644
--- a/mojo/application/application_runner_chromium.h
+++ b/mojo/application/application_runner_chromium.h
@@ -30,6 +30,8 @@
   explicit ApplicationRunnerChromium(ApplicationDelegate* delegate);
   ~ApplicationRunnerChromium();
 
+  static void InitBaseCommandLine();
+
   void set_message_loop_type(base::MessageLoop::Type type);
 
   // Once the various parameters have been set above, use Run to initialize an
diff --git a/mojo/application/application_test_base_chromium.cc b/mojo/application/application_test_base_chromium.cc
new file mode 100644
index 0000000..4d74f4f2
--- /dev/null
+++ b/mojo/application/application_test_base_chromium.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/application/application_test_base_chromium.h"
+
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+
+namespace mojo {
+namespace test {
+
+namespace {
+// Share the application URL with multiple application tests.
+String g_url;
+
+// Application request handle passed from the shell in MojoMain, stored in
+// between SetUp()/TearDown() so we can (re-)intialize new ApplicationImpls.
+InterfaceRequest<Application> g_application_request;
+
+// Shell pointer passed in the initial mojo.Application.Initialize() call,
+// stored in between initial setup and the first test and between SetUp/TearDown
+// calls so we can (re-)initialize new ApplicationImpls.
+ShellPtr g_shell;
+
+class ShellGrabber : public Application {
+ public:
+  explicit ShellGrabber(InterfaceRequest<Application> application_request)
+      : binding_(this, application_request.Pass()) {}
+
+  void WaitForInitialize() {
+    // Initialize is always the first call made on Application.
+    MOJO_CHECK(binding_.WaitForIncomingMethodCall());
+  }
+
+ private:
+  // Application implementation.
+  void Initialize(ShellPtr shell,
+                  Array<String> args,
+                  const mojo::String& url) override {
+    g_url = url;
+    g_application_request = binding_.Unbind();
+    g_shell = shell.Pass();
+  }
+
+  void AcceptConnection(const String& requestor_url,
+                        InterfaceRequest<ServiceProvider> services,
+                        ServiceProviderPtr exposed_services,
+                        const String& url) override {
+    MOJO_CHECK(false);
+  }
+
+  void RequestQuit() override { MOJO_CHECK(false); }
+
+  Binding<Application> binding_;
+};
+
+}  // namespace
+
+MojoResult RunAllTests(MojoHandle application_request_handle) {
+  {
+    // This loop is used for init, and then destroyed before running tests.
+    Environment::InstantiateDefaultRunLoop();
+
+    // Grab the shell handle.
+    ShellGrabber grabber(
+        MakeRequest<Application>(MakeScopedHandle(
+            MessagePipeHandle(application_request_handle))));
+    grabber.WaitForInitialize();
+    MOJO_CHECK(g_shell);
+    MOJO_CHECK(g_application_request.is_pending());
+
+    int argc = 0;
+    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+    const char** argv = new const char* [cmd_line->argv().size()];
+#if defined(OS_WIN)
+    std::vector<std::string> local_strings;
+#endif
+    for (auto& arg : cmd_line->argv()) {
+#if defined(OS_WIN)
+      local_strings.push_back(base::WideToUTF8(arg));
+      argv[argc++] = local_strings.back().c_str();
+#else
+      argv[argc++] = arg.c_str();
+#endif
+    }
+
+    testing::InitGoogleTest(&argc, const_cast<char**>(&(argv[0])));
+
+    Environment::DestroyDefaultRunLoop();
+  }
+
+  int result = RUN_ALL_TESTS();
+
+  // Shut down our message pipes before exiting.
+  (void)g_application_request.PassMessagePipe();
+  (void)g_shell.PassMessagePipe();
+
+  return (result == 0) ? MOJO_RESULT_OK : MOJO_RESULT_UNKNOWN;
+}
+
+ApplicationTestBase::ApplicationTestBase() : application_impl_(nullptr) {
+}
+
+ApplicationTestBase::~ApplicationTestBase() {
+}
+
+ApplicationDelegate* ApplicationTestBase::GetApplicationDelegate() {
+  return &default_application_delegate_;
+}
+
+void ApplicationTestBase::SetUp() {
+  // A run loop is recommended for ApplicationImpl initialization and
+  // communication.
+  if (ShouldCreateDefaultRunLoop())
+    Environment::InstantiateDefaultRunLoop();
+
+  MOJO_CHECK(g_application_request.is_pending());
+  MOJO_CHECK(g_shell);
+
+  // New applications are constructed for each test to avoid persisting state.
+  application_impl_ = new ApplicationImpl(GetApplicationDelegate(),
+                                          g_application_request.Pass());
+
+  // Fake application initialization with the given command line arguments.
+  Array<String> empty_args;
+  application_impl_->Initialize(g_shell.Pass(), empty_args.Clone(), g_url);
+}
+
+void ApplicationTestBase::TearDown() {
+  MOJO_CHECK(!g_application_request.is_pending());
+  MOJO_CHECK(!g_shell);
+
+  application_impl_->UnbindConnections(&g_application_request, &g_shell);
+  delete application_impl_;
+  if (ShouldCreateDefaultRunLoop())
+    Environment::DestroyDefaultRunLoop();
+}
+
+bool ApplicationTestBase::ShouldCreateDefaultRunLoop() {
+  return true;
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/application/application_test_base_chromium.h b/mojo/application/application_test_base_chromium.h
new file mode 100644
index 0000000..c050848
--- /dev/null
+++ b/mojo/application/application_test_base_chromium.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_APPLICATION_APPLICATION_TEST_BASE_CHROMIUM_H_
+#define MOJO_APPLICATION_APPLICATION_TEST_BASE_CHROMIUM_H_
+
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+class ApplicationImpl;
+
+namespace test {
+
+// Run all application tests. This must be called after the environment is
+// initialized, to support construction of a default run loop.
+MojoResult RunAllTests(MojoHandle application_request_handle);
+
+// A GTEST base class for application testing executed in mojo_shell.
+class ApplicationTestBase : public testing::Test {
+ public:
+  ApplicationTestBase();
+  ~ApplicationTestBase() override;
+
+ protected:
+  ApplicationImpl* application_impl() { return application_impl_; }
+
+  // Get the ApplicationDelegate for the application to be tested.
+  virtual ApplicationDelegate* GetApplicationDelegate();
+
+  // testing::Test:
+  void SetUp() override;
+  void TearDown() override;
+
+  // True by default, which indicates a MessageLoop will automatically be
+  // created for the application.  Tests may override this function to prevent
+  // a default loop from being created.
+  virtual bool ShouldCreateDefaultRunLoop();
+
+ private:
+  // The application implementation instance, reconstructed for each test.
+  ApplicationImpl* application_impl_;
+  // The application delegate used if GetApplicationDelegate is not overridden.
+  ApplicationDelegate default_application_delegate_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationTestBase);
+};
+
+}  // namespace test
+
+}  // namespace mojo
+
+#endif  // MOJO_APPLICATION_APPLICATION_TEST_BASE_CHROMIUM_H_
diff --git a/mojo/application/application_test_main_chromium.cc b/mojo/application/application_test_main_chromium.cc
index 4ce761a8..3d6c4b2 100644
--- a/mojo/application/application_test_main_chromium.cc
+++ b/mojo/application/application_test_main_chromium.cc
@@ -5,8 +5,9 @@
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/test/test_timeouts.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/c/system/main.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 
 MojoResult MojoMain(MojoHandle handle) {
   // An AtExitManager instance is needed to construct message loops.
@@ -14,7 +15,7 @@
 
   // Initialize test timeouts, which requires CommandLine::ForCurrentProcess().
   // TODO(msw): Plumb relevant command line args before initializing timeouts.
-  base::CommandLine::Init(0, nullptr);
+  mojo::ApplicationRunnerChromium::InitBaseCommandLine();
   TestTimeouts::Initialize();
 
   return mojo::test::RunAllTests(handle);
diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn
index bc6256df..945e6f05 100644
--- a/mojo/services/BUILD.gn
+++ b/mojo/services/BUILD.gn
@@ -21,7 +21,7 @@
     deps += [
       "//mojo/services/clipboard",
       "//mojo/services/html_viewer",
-      "//mojo/services/kiosk_wm",
+      "//mojo/services/kiosk_wm:window_manager",
       "//mojo/services/native_viewport",
       "//mojo/services/network",
       "//mojo/services/surfaces",
@@ -33,7 +33,7 @@
     if (is_mac) {
       deps -= [
         "//mojo/services/html_viewer",
-        "//mojo/services/kiosk_wm",
+        "//mojo/services/kiosk_wm:window_manager",
         "//mojo/services/native_viewport",
         "//mojo/services/view_manager",
       ]
diff --git a/mojo/services/clipboard/clipboard_apptest.cc b/mojo/services/clipboard/clipboard_apptest.cc
index b6a2fad..cb8f1e6 100644
--- a/mojo/services/clipboard/clipboard_apptest.cc
+++ b/mojo/services/clipboard/clipboard_apptest.cc
@@ -4,9 +4,9 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/common/common_type_converters.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "third_party/mojo_services/src/clipboard/public/interfaces/clipboard.mojom.h"
 
 using mojo::Array;
diff --git a/mojo/services/html_viewer/ax_provider_apptest.cc b/mojo/services/html_viewer/ax_provider_apptest.cc
index 29dc501..576b89a 100644
--- a/mojo/services/html_viewer/ax_provider_apptest.cc
+++ b/mojo/services/html_viewer/ax_provider_apptest.cc
@@ -6,8 +6,8 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/test_timeouts.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h"
diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc
index abe6379a..201343c 100644
--- a/mojo/services/html_viewer/html_viewer.cc
+++ b/mojo/services/html_viewer/html_viewer.cc
@@ -44,8 +44,7 @@
 
 namespace html_viewer {
 
-// Switches for html_viewer to be used with "--args-for". For example:
-// --args-for='mojo:html_viewer --enable-mojo-media-renderer'
+// Switches for html_viewer.
 
 // Enable MediaRenderer in media pipeline instead of using the internal
 // media::Renderer implementation.
@@ -183,16 +182,7 @@
 
     ui::RegisterPathProvider();
 
-    base::CommandLine::StringVector command_line_args;
-#if defined(OS_WIN)
-    for (const auto& arg : app->args())
-      command_line_args.push_back(base::UTF8ToUTF16(arg));
-#elif defined(OS_POSIX)
-    command_line_args = app->args();
-#endif
-
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    command_line->InitFromArgv(command_line_args);
 
     logging::LoggingSettings settings;
     settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
diff --git a/mojo/services/html_viewer/view_url.py b/mojo/services/html_viewer/view_url.py
deleted file mode 100755
index a543385..0000000
--- a/mojo/services/html_viewer/view_url.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import argparse
-import os
-import subprocess
-import sys
-
-root_path = os.path.realpath(
-    os.path.join(
-        os.path.dirname(
-            os.path.realpath(__file__)),
-        os.pardir,
-        os.pardir,
-        os.pardir))
-
-def _BuildShellCommand(args):
-  sdk_version = subprocess.check_output(["cat",
-      "third_party/mojo/src/mojo/public/VERSION"], cwd=root_path)
-  build_dir = os.path.join(root_path, args.build_dir)
-
-  shell_command = [os.path.join(build_dir, "mojo_shell")]
-
-  options = []
-  options.append(
-      "--origin=https://storage.googleapis.com/mojo/services/linux-x64/%s" %
-           sdk_version)
-  options.append("--url-mappings=mojo:html_viewer=file://%s/html_viewer.mojo" %
-                 build_dir)
-  options.append('--args-for=mojo:kiosk_wm %s' % args.url)
-
-  app_to_run = "mojo:kiosk_wm"
-
-  return shell_command + options + [app_to_run]
-
-def main():
-  parser = argparse.ArgumentParser(
-      description="View a URL with HTMLViewer in the Kiosk window manager. "
-                  "You must have built //mojo/services/html_viewer and "
-                  "//mojo/services/network first. Note that this will "
-                  "currently often fail spectacularly due to lack of binary "
-                  "stability in Mojo.")
-  parser.add_argument(
-      "--build-dir",
-      help="Path to the dir containing the linux-x64 binaries relative to the "
-           "repo root (default: %(default)s)",
-      default="out/Release")
-  parser.add_argument("url",
-                      help="The URL to be viewed")
-
-  args = parser.parse_args()
-  return subprocess.call(_BuildShellCommand(args))
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/mojo/services/kiosk_wm/BUILD.gn b/mojo/services/kiosk_wm/BUILD.gn
index 99c25f0d..988c6c9 100644
--- a/mojo/services/kiosk_wm/BUILD.gn
+++ b/mojo/services/kiosk_wm/BUILD.gn
@@ -5,7 +5,10 @@
 import("//third_party/mojo/src/mojo/public/mojo_application.gni")
 import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
 
-mojo_native_application("kiosk_wm") {
+# Mojo shell in chromium is only used for Mandoline, and Mandoline only uses
+# kiosk_wm, so we name the target window_manager to avoid having to remap on the
+# command line.
+mojo_native_application("window_manager") {
   sources = [
     "kiosk_wm.cc",
     "kiosk_wm.h",
diff --git a/mojo/services/kiosk_wm/kiosk_wm.cc b/mojo/services/kiosk_wm/kiosk_wm.cc
index 444003a..5c03ade 100644
--- a/mojo/services/kiosk_wm/kiosk_wm.cc
+++ b/mojo/services/kiosk_wm/kiosk_wm.cc
@@ -4,6 +4,8 @@
 
 #include "mojo/services/kiosk_wm/kiosk_wm.h"
 
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
 #include "mojo/services/kiosk_wm/merged_service_provider.h"
 #include "mojo/services/window_manager/basic_focus_rules.h"
 
@@ -28,9 +30,17 @@
 void KioskWM::Initialize(mojo::ApplicationImpl* app) {
   window_manager_app_->Initialize(app);
 
-  // Format: --args-for="app_url default_url"
-  if (app->args().size() > 1)
-    default_url_ = app->args()[1];
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::CommandLine::StringVector args = command_line->GetArgs();
+  if (args.empty()) {
+    default_url_ = "http://www.google.com/";
+  } else {
+#if defined(OS_WIN)
+    default_url_ = base::WideToUTF8(args[0]);
+#else
+    default_url_ = args[0];
+#endif
+  }
 }
 
 bool KioskWM::ConfigureIncomingConnection(
diff --git a/mojo/services/network/tcp_bound_socket_impl.cc b/mojo/services/network/tcp_bound_socket_impl.cc
index 8272b22..b7763045 100644
--- a/mojo/services/network/tcp_bound_socket_impl.cc
+++ b/mojo/services/network/tcp_bound_socket_impl.cc
@@ -104,11 +104,9 @@
 
 void TCPBoundSocketImpl::OnConnected(int result) {
   if (result == net::OK) {
-    BindToRequest(new TCPConnectedSocketImpl(
-                      socket_.Pass(),
-                      pending_connect_send_stream_.Pass(),
-                      pending_connect_receive_stream_.Pass()),
-                  &pending_connect_socket_);
+    new TCPConnectedSocketImpl(
+        socket_.Pass(), pending_connect_send_stream_.Pass(),
+        pending_connect_receive_stream_.Pass(), pending_connect_socket_.Pass());
   } else {
     pending_connect_send_stream_.reset();
     pending_connect_receive_stream_.reset();
diff --git a/mojo/services/network/tcp_connected_socket_impl.cc b/mojo/services/network/tcp_connected_socket_impl.cc
index 641f1c3..bc8c98b5 100644
--- a/mojo/services/network/tcp_connected_socket_impl.cc
+++ b/mojo/services/network/tcp_connected_socket_impl.cc
@@ -13,12 +13,17 @@
 TCPConnectedSocketImpl::TCPConnectedSocketImpl(
     scoped_ptr<net::TCPSocket> socket,
     ScopedDataPipeConsumerHandle send_stream,
-    ScopedDataPipeProducerHandle receive_stream)
+    ScopedDataPipeProducerHandle receive_stream,
+    InterfaceRequest<TCPConnectedSocket> request)
     : socket_(socket.Pass()),
       send_stream_(send_stream.Pass()),
       receive_stream_(receive_stream.Pass()),
+      binding_(this, request.Pass()),
       weak_ptr_factory_(this) {
   // Queue up async communication.
+  binding_.set_error_handler(this);
+  ListenForReceivePeerClosed();
+  ListenForSendPeerClosed();
   ReceiveMore();
   SendMore();
 }
@@ -26,6 +31,11 @@
 TCPConnectedSocketImpl::~TCPConnectedSocketImpl() {
 }
 
+void TCPConnectedSocketImpl::OnConnectionError() {
+  binding_.Close();
+  DeleteIfNeeded();
+}
+
 void TCPConnectedSocketImpl::ReceiveMore() {
   DCHECK(!pending_receive_.get());
 
@@ -35,8 +45,7 @@
   if (result == MOJO_RESULT_SHOULD_WAIT) {
     // The pipe is full. We need to wait for it to have more space.
     receive_handle_watcher_.Start(
-        receive_stream_.get(),
-        MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+        receive_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
         MOJO_DEADLINE_INDEFINITE,
         base::Bind(&TCPConnectedSocketImpl::OnReceiveStreamReady,
                    weak_ptr_factory_.GetWeakPtr()));
@@ -65,10 +74,10 @@
   CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
   scoped_refptr<net::IOBuffer> buf(
       new NetToMojoIOBuffer(pending_receive_.get()));
-  int read_result = socket_->Read(
-      buf.get(), static_cast<int>(num_bytes),
-      base::Bind(&TCPConnectedSocketImpl::DidReceive, base::Unretained(this),
-                 false));
+  int read_result =
+      socket_->Read(buf.get(), static_cast<int>(num_bytes),
+                    base::Bind(&TCPConnectedSocketImpl::DidReceive,
+                               weak_ptr_factory_.GetWeakPtr(), false));
   if (read_result == net::ERR_IO_PENDING) {
     // Pending I/O, wait for result in DidReceive().
   } else if (read_result > 0) {
@@ -90,11 +99,15 @@
     // net_result and mojo_result.
     return;
   }
+  ListenForReceivePeerClosed();
   ReceiveMore();
 }
 
 void TCPConnectedSocketImpl::DidReceive(bool completed_synchronously,
                                         int result) {
+  if (!pending_receive_)
+    return;
+
   if (result < 0) {
     // Error.
     ShutdownReceive();
@@ -110,17 +123,30 @@
   if (completed_synchronously) {
     // Don't recursively call ReceiveMore if this is a sync read.
     base::MessageLoop::current()->PostTask(
-        FROM_HERE,
-        base::Bind(&TCPConnectedSocketImpl::ReceiveMore,
-                   weak_ptr_factory_.GetWeakPtr()));
+        FROM_HERE, base::Bind(&TCPConnectedSocketImpl::ReceiveMore,
+                              weak_ptr_factory_.GetWeakPtr()));
   } else {
     ReceiveMore();
   }
 }
 
 void TCPConnectedSocketImpl::ShutdownReceive() {
+  receive_handle_watcher_.Stop();
   pending_receive_ = nullptr;
   receive_stream_.reset();
+  DeleteIfNeeded();
+}
+
+void TCPConnectedSocketImpl::ListenForReceivePeerClosed() {
+  receive_handle_watcher_.Start(
+      receive_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_DEADLINE_INDEFINITE,
+      base::Bind(&TCPConnectedSocketImpl::OnReceiveDataPipeClosed,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void TCPConnectedSocketImpl::OnReceiveDataPipeClosed(MojoResult result) {
+  ShutdownReceive();
 }
 
 void TCPConnectedSocketImpl::SendMore() {
@@ -130,8 +156,7 @@
   if (result == MOJO_RESULT_SHOULD_WAIT) {
     // Data not ready, wait for it.
     send_handle_watcher_.Start(
-        send_stream_.get(),
-        MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+        send_stream_.get(), MOJO_HANDLE_SIGNAL_READABLE,
         MOJO_DEADLINE_INDEFINITE,
         base::Bind(&TCPConnectedSocketImpl::OnSendStreamReady,
                    weak_ptr_factory_.GetWeakPtr()));
@@ -146,10 +171,10 @@
   // Got a buffer from Mojo, give it to the socket. Note that the sockets may
   // do partial writes.
   scoped_refptr<net::IOBuffer> buf(new MojoToNetIOBuffer(pending_send_.get()));
-  int write_result = socket_->Write(
-      buf.get(), static_cast<int>(num_bytes),
-      base::Bind(&TCPConnectedSocketImpl::DidSend, base::Unretained(this),
-                 false));
+  int write_result =
+      socket_->Write(buf.get(), static_cast<int>(num_bytes),
+                     base::Bind(&TCPConnectedSocketImpl::DidSend,
+                                weak_ptr_factory_.GetWeakPtr(), false));
   if (write_result == net::ERR_IO_PENDING) {
     // Pending I/O, wait for result in DidSend().
   } else if (write_result >= 0) {
@@ -170,11 +195,14 @@
     // net_result and mojo_result.
     return;
   }
+  ListenForSendPeerClosed();
   SendMore();
 }
 
-void TCPConnectedSocketImpl::DidSend(bool completed_synchronously,
-                                     int result) {
+void TCPConnectedSocketImpl::DidSend(bool completed_synchronously, int result) {
+  if (!pending_send_)
+    return;
+
   if (result < 0) {
     ShutdownSend();
     // TODO(johnmccutchan): Notify socket direction is closed along with
@@ -190,17 +218,37 @@
   if (completed_synchronously) {
     // Don't recursively call SendMore if this is a sync read.
     base::MessageLoop::current()->PostTask(
-        FROM_HERE,
-        base::Bind(&TCPConnectedSocketImpl::SendMore,
-                   weak_ptr_factory_.GetWeakPtr()));
+        FROM_HERE, base::Bind(&TCPConnectedSocketImpl::SendMore,
+                              weak_ptr_factory_.GetWeakPtr()));
   } else {
     SendMore();
   }
 }
 
 void TCPConnectedSocketImpl::ShutdownSend() {
+  send_handle_watcher_.Stop();
   pending_send_ = nullptr;
   send_stream_.reset();
+  DeleteIfNeeded();
+}
+
+void TCPConnectedSocketImpl::ListenForSendPeerClosed() {
+  send_handle_watcher_.Start(
+      send_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_DEADLINE_INDEFINITE,
+      base::Bind(&TCPConnectedSocketImpl::OnSendDataPipeClosed,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void TCPConnectedSocketImpl::OnSendDataPipeClosed(MojoResult result) {
+  ShutdownSend();
+}
+
+void TCPConnectedSocketImpl::DeleteIfNeeded() {
+  bool has_send = pending_send_ || send_stream_.is_valid();
+  bool has_receive = pending_receive_ || receive_stream_.is_valid();
+  if (!binding_.is_bound() && !has_send && !has_receive)
+    delete this;
 }
 
 }  // namespace mojo
diff --git a/mojo/services/network/tcp_connected_socket_impl.h b/mojo/services/network/tcp_connected_socket_impl.h
index a20f14fb..6e71499 100644
--- a/mojo/services/network/tcp_connected_socket_impl.h
+++ b/mojo/services/network/tcp_connected_socket_impl.h
@@ -10,27 +10,33 @@
 #include "mojo/common/handle_watcher.h"
 #include "mojo/services/network/public/interfaces/tcp_connected_socket.mojom.h"
 #include "net/socket/tcp_socket.h"
-#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_impl.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
 
 namespace mojo {
 
 class MojoToNetPendingBuffer;
 class NetToMojoPendingBuffer;
 
-class TCPConnectedSocketImpl : public InterfaceImpl<TCPConnectedSocket> {
+class TCPConnectedSocketImpl : public TCPConnectedSocket, public ErrorHandler {
  public:
   TCPConnectedSocketImpl(scoped_ptr<net::TCPSocket> socket,
                          ScopedDataPipeConsumerHandle send_stream,
-                         ScopedDataPipeProducerHandle receive_stream);
+                         ScopedDataPipeProducerHandle receive_stream,
+                         InterfaceRequest<TCPConnectedSocket> request);
   ~TCPConnectedSocketImpl() override;
 
  private:
+    // ErrorHandler methods:
+    void OnConnectionError() override;
+
   // "Receiving" in this context means reading from TCPSocket and writing to
   // the Mojo receive_stream.
   void ReceiveMore();
   void OnReceiveStreamReady(MojoResult result);
   void DidReceive(bool completed_synchronously, int result);
   void ShutdownReceive();
+  void ListenForReceivePeerClosed();
+  void OnReceiveDataPipeClosed(MojoResult result);
 
   // "Writing" is reading from the Mojo send_stream and writing to the
   // TCPSocket.
@@ -38,6 +44,10 @@
   void OnSendStreamReady(MojoResult result);
   void DidSend(bool completed_asynchronously, int result);
   void ShutdownSend();
+  void ListenForSendPeerClosed();
+  void OnSendDataPipeClosed(MojoResult result);
+
+  void DeleteIfNeeded();
 
   scoped_ptr<net::TCPSocket> socket_;
 
@@ -47,13 +57,16 @@
 
   // For reading from the network and writing to Mojo pipe.
   ScopedDataPipeConsumerHandle send_stream_;
-  common::HandleWatcher receive_handle_watcher_;
   scoped_refptr<NetToMojoPendingBuffer> pending_receive_;
+  common::HandleWatcher receive_handle_watcher_;
 
   // For reading from the Mojo pipe and writing to the network.
   ScopedDataPipeProducerHandle receive_stream_;
-  common::HandleWatcher send_handle_watcher_;
   scoped_refptr<MojoToNetPendingBuffer> pending_send_;
+  common::HandleWatcher send_handle_watcher_;
+
+  // To bind to the message pipe.
+  Binding<TCPConnectedSocket> binding_;
 
   base::WeakPtrFactory<TCPConnectedSocketImpl> weak_ptr_factory_;
 };
diff --git a/mojo/services/network/tcp_server_socket_impl.cc b/mojo/services/network/tcp_server_socket_impl.cc
index 3fb88e0..96ec1e3 100644
--- a/mojo/services/network/tcp_server_socket_impl.cc
+++ b/mojo/services/network/tcp_server_socket_impl.cc
@@ -55,10 +55,9 @@
     pending_receive_stream_.reset();
     pending_client_socket_ = InterfaceRequest<TCPConnectedSocket>();
   } else {
-    BindToRequest(new TCPConnectedSocketImpl(
-        accepted_socket_.Pass(),
-        pending_send_stream_.Pass(),
-        pending_receive_stream_.Pass()), &pending_client_socket_);
+    new TCPConnectedSocketImpl(
+        accepted_socket_.Pass(), pending_send_stream_.Pass(),
+        pending_receive_stream_.Pass(), pending_client_socket_.Pass());
     pending_callback_.Run(MakeNetworkError(net::OK),
                           NetAddress::From(accepted_address_));
   }
diff --git a/mojo/services/network/udp_socket_apptest.cc b/mojo/services/network/udp_socket_apptest.cc
index 9c783f6..d9aa204 100644
--- a/mojo/services/network/udp_socket_apptest.cc
+++ b/mojo/services/network/udp_socket_apptest.cc
@@ -5,9 +5,9 @@
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/public/cpp/bindings/callback.h"
 #include "mojo/services/network/public/cpp/udp_socket_wrapper.h"
 #include "mojo/services/network/public/interfaces/network_service.mojom.h"
diff --git a/mojo/services/network/url_loader_impl_apptest.cc b/mojo/services/network/url_loader_impl_apptest.cc
index 4cdbdfb..583fd3d5a 100644
--- a/mojo/services/network/url_loader_impl_apptest.cc
+++ b/mojo/services/network/url_loader_impl_apptest.cc
@@ -7,8 +7,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/common/message_pump_mojo.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/services/network/network_context.h"
 #include "mojo/services/network/url_loader_impl.h"
 #include "net/url_request/url_request_job.h"
diff --git a/mojo/services/view_manager/view_manager_client_apptest.cc b/mojo/services/view_manager/view_manager_client_apptest.cc
index 9d441d7..e197c9f 100644
--- a/mojo/services/view_manager/view_manager_client_apptest.cc
+++ b/mojo/services/view_manager/view_manager_client_apptest.cc
@@ -11,10 +11,10 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/test/test_timeouts.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/public/cpp/application/service_provider_impl.h"
 #include "third_party/mojo_services/src/geometry/public/cpp/geometry_util.h"
 #include "third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h"
diff --git a/mojo/services/view_manager/view_manager_service_apptest.cc b/mojo/services/view_manager/view_manager_service_apptest.cc
index 1237cc8..67ac384 100644
--- a/mojo/services/view_manager/view_manager_service_apptest.cc
+++ b/mojo/services/view_manager/view_manager_service_apptest.cc
@@ -5,9 +5,9 @@
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/services/view_manager/ids.h"
 #include "mojo/services/view_manager/test_change_tracker.h"
 #include "third_party/mojo_services/src/view_manager/public/interfaces/view_manager.mojom.h"
diff --git a/mojo/services/window_manager/BUILD.gn b/mojo/services/window_manager/BUILD.gn
index c34ebe1..bb6df15 100644
--- a/mojo/services/window_manager/BUILD.gn
+++ b/mojo/services/window_manager/BUILD.gn
@@ -6,23 +6,6 @@
 import("//third_party/mojo/src/mojo/public/mojo_application.gni")
 import("//testing/test.gni")
 
-mojo_native_application("window_manager") {
-  sources = [
-    "main.cc",
-  ]
-
-  public_deps = [
-    ":lib",
-  ]
-
-  deps = [
-    "//base",
-    "//mojo/application",
-    "//mojo/common:tracing_impl",
-    "//third_party/mojo_services/src/view_manager/public/cpp",
-  ]
-}
-
 source_set("lib") {
   sources = [
     "basic_focus_rules.cc",
@@ -108,6 +91,24 @@
   }
 }
 
+# A basic window manager with a default delegate used for testing.
+mojo_native_application("test_window_manager") {
+  sources = [
+    "main.cc",
+  ]
+
+  public_deps = [
+    ":lib",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common:tracing_impl",
+    "//third_party/mojo_services/src/view_manager/public/cpp",
+  ]
+}
+
 mojo_native_application("window_manager_apptests") {
   testonly = true
 
@@ -125,5 +126,5 @@
     "//third_party/mojo_services/src/window_manager/public/interfaces",
   ]
 
-  data_deps = [ ":window_manager($default_toolchain)" ]
+  data_deps = [ ":test_window_manager($default_toolchain)" ]
 }
diff --git a/mojo/services/window_manager/window_manager_apptest.cc b/mojo/services/window_manager/window_manager_apptest.cc
index 7f82211..390c037 100644
--- a/mojo/services/window_manager/window_manager_apptest.cc
+++ b/mojo/services/window_manager/window_manager_apptest.cc
@@ -4,9 +4,9 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/public/cpp/application/service_provider_impl.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
@@ -84,7 +84,7 @@
   // ApplicationTestBase:
   void SetUp() override {
     ApplicationTestBase::SetUp();
-    application_impl()->ConnectToService("mojo:window_manager",
+    application_impl()->ConnectToService("mojo:test_window_manager",
                                          &window_manager_);
   }
   ApplicationDelegate* GetApplicationDelegate() override {
diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn
index 4643e6b..10c5b2c0e 100644
--- a/mojo/shell/BUILD.gn
+++ b/mojo/shell/BUILD.gn
@@ -112,8 +112,6 @@
     "child_process.h",
     "child_process_host.cc",
     "child_process_host.h",
-    "command_line_util.cc",
-    "command_line_util.h",
     "context.cc",
     "context.h",
     "filename_util.cc",
@@ -361,7 +359,6 @@
 test("mojo_shell_tests") {
   sources = [
     "child_process_host_unittest.cc",
-    "command_line_util_unittest.cc",
     "data_pipe_peek_unittest.cc",
     "in_process_native_runner_unittest.cc",
     "native_runner_unittest.cc",
diff --git a/mojo/shell/android/main.cc b/mojo/shell/android/main.cc
index 41c507aa..9caf840 100644
--- a/mojo/shell/android/main.cc
+++ b/mojo/shell/android/main.cc
@@ -26,7 +26,6 @@
 #include "mojo/shell/android/native_viewport_application_loader.h"
 #include "mojo/shell/android/ui_application_loader_android.h"
 #include "mojo/shell/application_manager/application_loader.h"
-#include "mojo/shell/command_line_util.h"
 #include "mojo/shell/context.h"
 #include "mojo/shell/init.h"
 #include "ui/gl/gl_surface_egl.h"
@@ -46,15 +45,12 @@
 
 class MojoShellRunner : public base::DelegateSimpleThread::Delegate {
  public:
-  MojoShellRunner(const std::vector<std::string>& parameters)
-      : parameters_(parameters) {}
+  MojoShellRunner(const std::vector<std::string>& parameters) {}
   ~MojoShellRunner() override {}
 
  private:
   void Run() override;
 
-  std::vector<std::string> parameters_;
-
   DISALLOW_COPY_AND_ASSIGN(MojoShellRunner);
 };
 
@@ -107,10 +103,7 @@
   ConfigureAndroidServices(context);
   context->Init();
 
-  for (auto& args : parameters_)
-    ApplyApplicationArgs(context, args);
-
-  RunCommandLineApps(context);
+  context->Run(GURL("mojo:window_manager"));
   loop.Run();
 
   g_java_message_loop.Pointer()->get()->PostTask(FROM_HERE,
diff --git a/mojo/shell/application_manager/application_manager.cc b/mojo/shell/application_manager/application_manager.cc
index d137fb8..8c908a7 100644
--- a/mojo/shell/application_manager/application_manager.cc
+++ b/mojo/shell/application_manager/application_manager.cc
@@ -29,17 +29,6 @@
 // Used by TestAPI.
 bool has_created_instance = false;
 
-std::vector<std::string> Concatenate(const std::vector<std::string>& v1,
-                                     const std::vector<std::string>& v2) {
-  if (!v1.size())
-    return v2;
-  if (!v2.size())
-    return v1;
-  std::vector<std::string> result(v1);
-  result.insert(result.end(), v1.begin(), v1.end());
-  return result;
-}
-
 }  // namespace
 
 ApplicationManager::Delegate::~Delegate() {
@@ -154,32 +143,31 @@
   }
 
   // The application is not running, let's compute the parameters.
-  std::vector<std::string> parameters =
-      Concatenate(pre_redirect_parameters, GetArgsForURL(resolved_url));
-
   if (ConnectToApplicationWithLoader(mapped_url, requestor_url, &services,
                                      &exposed_services, on_application_end,
-                                     parameters, GetLoaderForURL(mapped_url))) {
-    return;
-  }
-
-  if (ConnectToApplicationWithLoader(
-          resolved_url, requestor_url, &services, &exposed_services,
-          on_application_end, parameters, GetLoaderForURL(resolved_url))) {
+                                     pre_redirect_parameters,
+                                     GetLoaderForURL(mapped_url))) {
     return;
   }
 
   if (ConnectToApplicationWithLoader(resolved_url, requestor_url, &services,
                                      &exposed_services, on_application_end,
-                                     parameters, default_loader_.get())) {
+                                     pre_redirect_parameters,
+                                     GetLoaderForURL(resolved_url))) {
     return;
   }
 
-  auto callback = base::Bind(
-      &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
-      requestor_url, base::Passed(services.Pass()),
-      base::Passed(exposed_services.Pass()), on_application_end,
-      parameters);
+  if (ConnectToApplicationWithLoader(
+          resolved_url, requestor_url, &services, &exposed_services,
+          on_application_end, pre_redirect_parameters, default_loader_.get())) {
+    return;
+  }
+
+  auto callback = base::Bind(&ApplicationManager::HandleFetchCallback,
+                             weak_ptr_factory_.GetWeakPtr(), requestor_url,
+                             base::Passed(services.Pass()),
+                             base::Passed(exposed_services.Pass()),
+                             on_application_end, pre_redirect_parameters);
 
   if (resolved_url.SchemeIsFile()) {
     new LocalFetcher(
@@ -375,22 +363,6 @@
                            weak_ptr_factory_.GetWeakPtr(), runner));
 }
 
-void ApplicationManager::RegisterExternalApplication(
-    const GURL& url,
-    const std::vector<std::string>& args,
-    ApplicationPtr application) {
-  const auto& args_it = url_to_args_.find(url);
-  if (args_it != url_to_args_.end()) {
-    LOG(WARNING) << "--args-for provided for external application " << url
-                 << " <ignored>";
-  }
-  Identity identity(url);
-  ShellImpl* shell_impl =
-      new ShellImpl(application.Pass(), this, identity, base::Closure());
-  identity_to_shell_impl_[identity] = shell_impl;
-  shell_impl->InitializeApplication(Array<String>::From(args));
-}
-
 void ApplicationManager::RegisterContentHandler(
     const std::string& mime_type,
     const GURL& content_handler_url) {
@@ -434,21 +406,6 @@
   scheme_to_loader_[scheme] = loader.release();
 }
 
-void ApplicationManager::SetArgsForURL(const std::vector<std::string>& args,
-                                       const GURL& url) {
-  url_to_args_[url].insert(url_to_args_[url].end(), args.begin(), args.end());
-  GURL mapped_url = delegate_->ResolveMappings(url);
-  if (mapped_url != url) {
-    url_to_args_[mapped_url].insert(url_to_args_[mapped_url].end(),
-                                    args.begin(), args.end());
-  }
-  GURL resolved_url = delegate_->ResolveURL(mapped_url);
-  if (resolved_url != mapped_url) {
-    url_to_args_[resolved_url].insert(url_to_args_[resolved_url].end(),
-                                      args.begin(), args.end());
-  }
-}
-
 void ApplicationManager::SetNativeOptionsForURL(
     const NativeRunnerFactory::Options& options,
     const GURL& url) {
@@ -507,13 +464,6 @@
   return pipe.handle0.Pass();
 }
 
-std::vector<std::string> ApplicationManager::GetArgsForURL(const GURL& url) {
-  const auto& args_it = url_to_args_.find(url);
-  if (args_it != url_to_args_.end())
-    return args_it->second;
-  return std::vector<std::string>();
-}
-
 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
   native_runners_.erase(
       std::find(native_runners_.begin(), native_runners_.end(), runner));
diff --git a/mojo/shell/application_manager/application_manager.h b/mojo/shell/application_manager/application_manager.h
index d0d3a40..44e581d 100644
--- a/mojo/shell/application_manager/application_manager.h
+++ b/mojo/shell/application_manager/application_manager.h
@@ -83,10 +83,6 @@
   void RegisterContentHandler(const std::string& mime_type,
                               const GURL& content_handler_url);
 
-  void RegisterExternalApplication(const GURL& application_url,
-                                   const std::vector<std::string>& args,
-                                   ApplicationPtr application);
-
   // Sets the default Loader to be used if not overridden by SetLoaderForURL()
   // or SetLoaderForScheme().
   void set_default_loader(scoped_ptr<ApplicationLoader> loader) {
@@ -105,11 +101,6 @@
   // Sets a Loader to be used for a specific url scheme.
   void SetLoaderForScheme(scoped_ptr<ApplicationLoader> loader,
                           const std::string& scheme);
-  // These strings will be passed to the Initialize() method when an Application
-  // is instantiated.
-  // TODO(vtl): Maybe we should store/compare resolved URLs, like
-  // SetNativeOptionsForURL() below?
-  void SetArgsForURL(const std::vector<std::string>& args, const GURL& url);
   // These options will be used in running any native application at |url|
   // (which shouldn't contain a query string). (|url| will be mapped and
   // resolved, and any application whose base resolved URL matches it will have
@@ -135,7 +126,6 @@
   typedef std::map<GURL, ApplicationLoader*> URLToLoaderMap;
   typedef std::map<Identity, ShellImpl*> IdentityToShellImplMap;
   typedef std::map<GURL, ContentHandlerConnection*> URLToContentHandlerMap;
-  typedef std::map<GURL, std::vector<std::string>> URLToArgsMap;
   typedef std::map<std::string, GURL> MimeTypeToURLMap;
   typedef std::map<GURL, NativeRunnerFactory::Options> URLToNativeOptionsMap;
 
@@ -204,9 +194,6 @@
   // Removes a ContentHandler when it encounters an error.
   void OnContentHandlerError(ContentHandlerConnection* content_handler);
 
-  // Returns the arguments for the given url.
-  std::vector<std::string> GetArgsForURL(const GURL& url);
-
   void CleanupRunner(NativeRunner* runner);
 
   Delegate* const delegate_;
@@ -219,7 +206,6 @@
 
   IdentityToShellImplMap identity_to_shell_impl_;
   URLToContentHandlerMap url_to_content_handler_;
-  URLToArgsMap url_to_args_;
   // Note: The keys are URLs after mapping and resolving.
   URLToNativeOptionsMap url_to_native_options_;
 
diff --git a/mojo/shell/application_manager/application_manager_unittest.cc b/mojo/shell/application_manager/application_manager_unittest.cc
index ed16d15..cc420a7 100644
--- a/mojo/shell/application_manager/application_manager_unittest.cc
+++ b/mojo/shell/application_manager/application_manager_unittest.cc
@@ -419,34 +419,6 @@
   std::map<GURL, GURL> mappings_;
 };
 
-class TestExternal : public ApplicationDelegate {
- public:
-  TestExternal() : configure_incoming_connection_called_(false) {}
-
-  void Initialize(ApplicationImpl* app) override {
-    initialize_args_ = app->args();
-    base::MessageLoop::current()->Quit();
-  }
-
-  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
-    configure_incoming_connection_called_ = true;
-    base::MessageLoop::current()->Quit();
-    return true;
-  }
-
-  const std::vector<std::string>& initialize_args() const {
-    return initialize_args_;
-  }
-
-  bool configure_incoming_connection_called() const {
-    return configure_incoming_connection_called_;
-  }
-
- private:
-  std::vector<std::string> initialize_args_;
-  bool configure_incoming_connection_called_;
-};
-
 class ApplicationManagerTest : public testing::Test {
  public:
   ApplicationManagerTest() : tester_context_(&loop_) {}
@@ -515,75 +487,6 @@
   EXPECT_EQ(0U, app_args.size());
 }
 
-// Confirm that arguments are sent to an application.
-TEST_F(ApplicationManagerTest, Args) {
-  ApplicationManager am(&test_delegate_);
-  GURL test_url("test:test");
-  std::vector<std::string> args;
-  args.push_back("test_arg1");
-  args.push_back("test_arg2");
-  am.SetArgsForURL(args, test_url);
-  TestApplicationLoader* loader = new TestApplicationLoader;
-  loader->set_context(&context_);
-  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
-  TestServicePtr test_service;
-  am.ConnectToService(test_url, &test_service);
-  TestClient test_client(test_service.Pass());
-  test_client.Test("test");
-  loop_.Run();
-  std::vector<std::string> app_args = loader->GetArgs();
-  ASSERT_EQ(args.size(), app_args.size());
-  EXPECT_EQ(args[0], app_args[0]);
-  EXPECT_EQ(args[1], app_args[1]);
-}
-
-// Confirm that arguments are aggregated through mappings.
-TEST_F(ApplicationManagerTest, ArgsAndMapping) {
-  ApplicationManager am(&test_delegate_);
-  GURL test_url("test:test");
-  GURL test_url2("test:test2");
-  test_delegate_.AddMapping(test_url, test_url2);
-  std::vector<std::string> args;
-  args.push_back("test_arg1");
-  args.push_back("test_arg2");
-  am.SetArgsForURL(args, test_url);
-  std::vector<std::string> args2;
-  args2.push_back("test_arg3");
-  args2.push_back("test_arg4");
-  am.SetArgsForURL(args2, test_url2);
-  TestApplicationLoader* loader = new TestApplicationLoader;
-  loader->set_context(&context_);
-  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url2);
-  {
-    // Connext to the mapped url
-    TestServicePtr test_service;
-    am.ConnectToService(test_url, &test_service);
-    TestClient test_client(test_service.Pass());
-    test_client.Test("test");
-    loop_.Run();
-    std::vector<std::string> app_args = loader->GetArgs();
-    ASSERT_EQ(args.size() + args2.size(), app_args.size());
-    EXPECT_EQ(args[0], app_args[0]);
-    EXPECT_EQ(args[1], app_args[1]);
-    EXPECT_EQ(args2[0], app_args[2]);
-    EXPECT_EQ(args2[1], app_args[3]);
-  }
-  {
-    // Connext to the target url
-    TestServicePtr test_service;
-    am.ConnectToService(test_url2, &test_service);
-    TestClient test_client(test_service.Pass());
-    test_client.Test("test");
-    loop_.Run();
-    std::vector<std::string> app_args = loader->GetArgs();
-    ASSERT_EQ(args.size() + args2.size(), app_args.size());
-    EXPECT_EQ(args[0], app_args[0]);
-    EXPECT_EQ(args[1], app_args[1]);
-    EXPECT_EQ(args2[0], app_args[2]);
-    EXPECT_EQ(args2[1], app_args[3]);
-  }
-}
-
 TEST_F(ApplicationManagerTest, ClientError) {
   test_client_->Test("test");
   EXPECT_TRUE(HasFactoryForTestURL());
@@ -773,22 +676,6 @@
   custom_loader->set_context(nullptr);
 }
 
-TEST_F(ApplicationManagerTest, ExternalApp) {
-  ApplicationPtr application;
-  TestExternal external;
-  std::vector<std::string> args;
-  args.push_back("test");
-  ApplicationImpl app(&external, GetProxy(&application));
-  application_manager_->RegisterExternalApplication(GURL("mojo:test"), args,
-                                                    application.Pass());
-  loop_.Run();
-  EXPECT_EQ(args, external.initialize_args());
-  application_manager_->ConnectToServiceByName(GURL("mojo:test"),
-                                               std::string());
-  loop_.Run();
-  EXPECT_TRUE(external.configure_incoming_connection_called());
-};
-
 TEST_F(ApplicationManagerTest, TestQueryWithLoaders) {
   TestApplicationLoader* url_loader = new TestApplicationLoader;
   TestApplicationLoader* scheme_loader = new TestApplicationLoader;
diff --git a/mojo/shell/command_line_util.cc b/mojo/shell/command_line_util.cc
deleted file mode 100644
index 68bdfa4d..0000000
--- a/mojo/shell/command_line_util.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/shell/command_line_util.h"
-
-#include <functional>
-
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/strings/string_split.h"
-#include "base/strings/utf_string_conversions.h"
-#include "mojo/shell/context.h"
-#include "mojo/shell/switches.h"
-
-namespace mojo {
-namespace shell {
-
-namespace {
-GURL GetAppURLAndSetArgs(const std::string& app_url_and_args,
-                         Context* context) {
-  std::vector<std::string> args;
-  GURL app_url = GetAppURLAndArgs(context, app_url_and_args, &args);
-
-  if (args.size() > 1)
-    context->application_manager()->SetArgsForURL(args, app_url);
-  return app_url;
-}
-}  // namespace
-
-bool ParseArgsFor(const std::string& arg, std::string* value) {
-  const std::string kArgsForSwitches[] = {
-      "-" + std::string(switches::kArgsFor) + "=",
-      "--" + std::string(switches::kArgsFor) + "=",
-  };
-  for (size_t i = 0; i < arraysize(kArgsForSwitches); i++) {
-    const std::string& argsfor_switch = kArgsForSwitches[i];
-    if (arg.compare(0, argsfor_switch.size(), argsfor_switch) == 0) {
-      *value = arg.substr(argsfor_switch.size(), std::string::npos);
-      return true;
-    }
-  }
-  return false;
-}
-
-GURL GetAppURLAndArgs(Context* context,
-                      const std::string& app_url_and_args,
-                      std::vector<std::string>* args) {
-  // SplitString() returns empty strings for extra delimeter characters (' ').
-  base::SplitString(app_url_and_args, ' ', args);
-  args->erase(std::remove_if(args->begin(), args->end(),
-                             std::mem_fun_ref(&std::string::empty)),
-              args->end());
-
-  if (args->empty())
-    return GURL();
-  GURL app_url = context->ResolveCommandLineURL((*args)[0]);
-  if (!app_url.is_valid()) {
-    LOG(ERROR) << "Error: invalid URL: " << (*args)[0];
-    return app_url;
-  }
-  if (args->size() == 1)
-    args->clear();
-  return app_url;
-}
-
-void ApplyApplicationArgs(Context* context, const std::string& args) {
-  std::string args_for_value;
-  if (ParseArgsFor(args, &args_for_value))
-    GetAppURLAndSetArgs(args_for_value, context);
-}
-
-void RunCommandLineApps(Context* context) {
-  const auto& command_line = *base::CommandLine::ForCurrentProcess();
-  for (const auto& arg : command_line.GetArgs()) {
-    std::string arg2;
-#if defined(OS_WIN)
-    arg2 = base::UTF16ToUTF8(arg);
-#else
-    arg2 = arg;
-#endif
-    GURL url = GetAppURLAndSetArgs(arg2, context);
-    if (!url.is_valid())
-      return;
-    context->Run(url);
-  }
-}
-
-}  // namespace shell
-}  // namespace mojo
diff --git a/mojo/shell/command_line_util.h b/mojo/shell/command_line_util.h
deleted file mode 100644
index aa80002..0000000
--- a/mojo/shell/command_line_util.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SHELL_COMMAND_LINE_UTIL_H_
-#define SHELL_COMMAND_LINE_UTIL_H_
-
-#include "mojo/shell/context.h"
-
-namespace mojo {
-namespace shell {
-
-// Parse the given arg, looking for an --args-for switch. If this is not the
-// case, returns |false|. Otherwise, returns |true| and set |*value| to the
-// value of the switch.
-bool ParseArgsFor(const std::string& arg, std::string* value);
-
-// The value of app_url_and_args is "<mojo_app_url> [<args>...]", where args
-// is a list of "configuration" arguments separated by spaces. If one or more
-// arguments are specified they will be available when the Mojo application
-// is initialized. This returns the mojo_app_url, and set args to the list of
-// arguments.
-GURL GetAppURLAndArgs(Context* context,
-                      const std::string& app_url_and_args,
-                      std::vector<std::string>* args);
-
-// Apply arguments for an application from a line with the following format:
-// '--args-for=application_url arg1 arg2 arg3'
-// This does nothing if the line has not the right format.
-void ApplyApplicationArgs(Context* context, const std::string& args);
-
-// Run all application defined on the command line, using the given context.
-void RunCommandLineApps(Context* context);
-
-}  // namespace shell
-}  // namespace mojo
-
-#endif  // SHELL_COMMAND_LINE_UTIL_H_
diff --git a/mojo/shell/command_line_util_unittest.cc b/mojo/shell/command_line_util_unittest.cc
deleted file mode 100644
index 547aa86..0000000
--- a/mojo/shell/command_line_util_unittest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/shell/command_line_util.h"
-
-#include "base/logging.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace shell {
-namespace {
-
-TEST(CommandLineUtil, ParseArgsFor) {
-  static const struct Expectation {
-    const char* args;
-    const char* value;
-  } EXPECTATIONS[] = {
-      {"", nullptr},
-      {"hello", nullptr},
-      {"args-for=mojo:app1", nullptr},
-      {"--args-for", nullptr},
-      {"--args-for=", ""},
-      {"--args-for=mojo:app1", "mojo:app1"},
-      {"--args-for=mojo:app1 hello world", "mojo:app1 hello world"},
-      {"-args-for", nullptr},
-      {"-args-for=", ""},
-      {"-args-for=mojo:app1", "mojo:app1"},
-      {"-args-for=mojo:app1 hello world", "mojo:app1 hello world"}};
-  for (auto& expectation : EXPECTATIONS) {
-    std::string value;
-    bool result = ParseArgsFor(expectation.args, &value);
-    EXPECT_EQ(bool(expectation.value), result);
-    if (expectation.value && result)
-      EXPECT_EQ(value, expectation.value);
-  }
-}
-
-TEST(CommandLineUtil, GetAppURLAndArgs) {
-  const char* NO_ARGUMENTS[] = {nullptr};
-  const char* ONE_ARGUMENTS[] = {"1", nullptr};
-  const char* TWO_ARGUMENTS[] = {"1", "two", nullptr};
-  static const struct Expectation {
-    const char* args;
-    const char* url;
-    const char** values;
-  } EXPECTATIONS[] = {
-      {"", nullptr, nullptr},
-      {"foo", "file:///root/foo", NO_ARGUMENTS},
-      {"/foo", "file:///foo", NO_ARGUMENTS},
-      {"file:foo", "file:///root/foo", NO_ARGUMENTS},
-      {"file:///foo", "file:///foo", NO_ARGUMENTS},
-      {"http://example.com", "http://example.com", NO_ARGUMENTS},
-      {"http://example.com 1", "http://example.com", ONE_ARGUMENTS},
-      {"http://example.com 1 ", "http://example.com", ONE_ARGUMENTS},
-      {"http://example.com  1 ", "http://example.com", ONE_ARGUMENTS},
-      {"http://example.com 1 two", "http://example.com", TWO_ARGUMENTS},
-      {"   http://example.com  1  two   ",
-       "http://example.com",
-       TWO_ARGUMENTS}};
-  Context context;
-  context.SetCommandLineCWD(base::FilePath(FILE_PATH_LITERAL("/root")));
-  for (auto& expectation : EXPECTATIONS) {
-    std::vector<std::string> args;
-    GURL result(GetAppURLAndArgs(&context, expectation.args, &args));
-    EXPECT_EQ(bool(expectation.url), result.is_valid());
-    if (expectation.url && result.is_valid()) {
-      EXPECT_EQ(GURL(expectation.url), result);
-      std::vector<std::string> expected_args;
-      if (expectation.values) {
-        if (*expectation.values)
-          expected_args.push_back(expectation.url);
-        for (const char** value = expectation.values; *value; ++value)
-          expected_args.push_back(*value);
-      }
-      EXPECT_EQ(expected_args, args);
-    }
-  }
-}
-
-}  // namespace
-}  // namespace shell
-}  // namespace mojo
diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc
index fbde07d..fc56476f 100644
--- a/mojo/shell/context.cc
+++ b/mojo/shell/context.cc
@@ -30,7 +30,6 @@
 #include "mojo/services/tracing/tracing.mojom.h"
 #include "mojo/shell/application_manager/application_loader.h"
 #include "mojo/shell/application_manager/application_manager.h"
-#include "mojo/shell/command_line_util.h"
 #include "mojo/shell/filename_util.h"
 #include "mojo/shell/in_process_native_runner.h"
 #include "mojo/shell/out_of_process_native_runner.h"
@@ -85,22 +84,6 @@
     resolver->AddOriginMapping(GURL(origin_mapping.origin),
                                GURL(origin_mapping.base_url));
 
-  if (command_line.HasSwitch(switches::kURLMappings)) {
-    const std::string mappings =
-        command_line.GetSwitchValueASCII(switches::kURLMappings);
-
-    base::StringPairs pairs;
-    if (!base::SplitStringIntoKeyValuePairs(mappings, '=', ',', &pairs))
-      return false;
-    using StringPair = std::pair<std::string, std::string>;
-    for (const StringPair& pair : pairs) {
-      const GURL from(pair.first);
-      const GURL to = context->ResolveCommandLineURL(pair.second);
-      if (!from.is_valid() || !to.is_valid())
-        return false;
-      resolver->AddURLMapping(from, to);
-    }
-  }
   return true;
 }
 
diff --git a/mojo/shell/desktop/launcher_process.cc b/mojo/shell/desktop/launcher_process.cc
index f6bb6ded..1b47cc8 100644
--- a/mojo/shell/desktop/launcher_process.cc
+++ b/mojo/shell/desktop/launcher_process.cc
@@ -15,7 +15,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
-#include "mojo/shell/command_line_util.h"
 #include "mojo/shell/context.h"
 #include "mojo/shell/switches.h"
 
@@ -23,29 +22,6 @@
 namespace shell {
 namespace {
 
-void Usage() {
-  std::cerr << "Launch Mojo applications.\n";
-  std::cerr
-      << "Usage: mojo_shell"
-      << " [--" << switches::kArgsFor << "=<mojo-app>]"
-      << " [--" << switches::kContentHandlers << "=<handlers>]"
-      << " [--" << switches::kDisableCache << "]"
-      << " [--" << switches::kEnableMultiprocess << "]"
-      << " [--" << switches::kOrigin << "=<url-lib-path>]"
-      << " [--" << switches::kTraceStartup << "]"
-      << " [--" << switches::kURLMappings << "=from1=to1,from2=to2]"
-      << " [--" << switches::kPredictableAppFilenames << "]"
-      << " [--" << switches::kWaitForDebugger << "]"
-      << " <mojo-app> ...\n\n"
-      << "A <mojo-app> is a Mojo URL or a Mojo URL and arguments within "
-      << "quotes.\n"
-      << "Example: mojo_shell \"mojo:js_standalone test.js\".\n"
-      << "<url-lib-path> is searched for shared libraries named by mojo URLs.\n"
-      << "The value of <handlers> is a comma separated list like:\n"
-      << "text/html,mojo:html_viewer,"
-      << "application/javascript,mojo:js_content_handler\n";
-}
-
 // Whether we're currently tracing.
 bool g_tracing = false;
 
@@ -99,28 +75,28 @@
   flush_complete_event.Wait();
 }
 
+void StartApp(mojo::shell::Context* context) {
+  // If a mojo app isn't specified (i.e. for an apptest), run the mojo shell's
+  // window manager.
+  GURL app_url(GURL("mojo:window_manager"));
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::CommandLine::StringVector args = command_line->GetArgs();
+  for (size_t i = 0; i < args.size(); ++i) {
+    GURL possible_app(args[i]);
+    if (possible_app.SchemeIs("mojo")) {
+      app_url = possible_app;
+      break;
+    }
+  }
+
+  context->Run(app_url);
+}
+
 }  // namespace
 
 int LauncherProcessMain(int argc, char** argv) {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
-
-  const std::set<std::string> all_switches = switches::GetAllSwitches();
-  const base::CommandLine::SwitchMap switches = command_line.GetSwitches();
-  bool found_unknown_switch = false;
-  for (const auto& s : switches) {
-    if (all_switches.find(s.first) == all_switches.end()) {
-      std::cerr << "unknown switch: " << s.first << std::endl;
-      found_unknown_switch = true;
-    }
-  }
-
-  if (found_unknown_switch || command_line.HasSwitch(switches::kHelp) ||
-      command_line.GetArgs().empty()) {
-    Usage();
-    return 0;
-  }
-
   if (command_line.HasSwitch(switches::kTraceStartup)) {
     g_tracing = true;
     base::trace_event::CategoryFilter category_filter(
@@ -136,7 +112,6 @@
   {
     base::MessageLoop message_loop;
     if (!shell_context.Init()) {
-      Usage();
       return 0;
     }
     if (g_tracing) {
@@ -145,16 +120,7 @@
                                    base::TimeDelta::FromSeconds(5));
     }
 
-    // The mojo_shell --args-for command-line switch is handled specially
-    // because it can appear more than once. The base::CommandLine class
-    // collapses multiple occurrences of the same switch.
-    for (int i = 1; i < argc; i++) {
-      ApplyApplicationArgs(&shell_context, argv[i]);
-    }
-
-    message_loop.PostTask(
-        FROM_HERE,
-        base::Bind(&mojo::shell::RunCommandLineApps, &shell_context));
+    message_loop.PostTask(FROM_HERE, base::Bind(&StartApp, &shell_context));
     message_loop.Run();
 
     // Must be called before |message_loop| is destroyed.
diff --git a/mojo/shell/native_application_support.cc b/mojo/shell/native_application_support.cc
index 55ae2cf..9df3ed88 100644
--- a/mojo/shell/native_application_support.cc
+++ b/mojo/shell/native_application_support.cc
@@ -4,6 +4,7 @@
 
 #include "mojo/shell/native_application_support.h"
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -104,6 +105,26 @@
     init_go_runtime();
   }
 
+#if !defined(OS_WIN)
+  // On Windows, initializing base::CommandLine with null parameters gets the
+  // process's command line from the OS. Other platforms need it to be passed
+  // in. This needs to be passed in before the app initializes the command line,
+  // which is done as soon as it loads.
+  typedef void (*InitCommandLineArgs)(int, const char* const*);
+  InitCommandLineArgs init_command_line_args =
+      reinterpret_cast<InitCommandLineArgs>(
+          base::GetFunctionPointerFromNativeLibrary(app_library,
+                                                    "InitCommandLineArgs"));
+  if (init_command_line_args) {
+    int argc = 0;
+    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+    const char** argv = new const char* [cmd_line->argv().size()];
+    for (auto& arg : cmd_line->argv())
+      argv[argc++] = arg.c_str();
+    init_command_line_args(argc, argv);
+  }
+#endif
+
   typedef MojoResult (*MojoMainFunction)(MojoHandle);
   MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
       base::GetFunctionPointerFromNativeLibrary(app_library, "MojoMain"));
diff --git a/mojo/shell/shell_apptest.cc b/mojo/shell/shell_apptest.cc
index 1acdf7d..66b6889 100644
--- a/mojo/shell/shell_apptest.cc
+++ b/mojo/shell/shell_apptest.cc
@@ -10,9 +10,9 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "mojo/application/application_test_base_chromium.h"
 #include "mojo/common/data_pipe_utils.h"
 #include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
 #include "mojo/public/cpp/system/macros.h"
 #include "mojo/services/http_server/public/cpp/http_server_util.h"
 #include "mojo/services/http_server/public/interfaces/http_server.mojom.h"
diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc
index 2150b19..28e7d058 100644
--- a/mojo/shell/switches.cc
+++ b/mojo/shell/switches.cc
@@ -8,18 +8,6 @@
 
 namespace switches {
 
-namespace {
-// This controls logging verbosity. It's not strictly a switch for mojo_shell,
-// and isn't included in the public switches, but is included here so that it
-// doesn't trigger an error at startup.
-const char kV[] = "v";
-
-}  // namespace
-
-// Specify configuration arguments for a Mojo application URL. For example:
-// --args-for='mojo:wget http://www.google.com'
-const char kArgsFor[] = "args-for";
-
 // Used internally by the main process to indicate that a new process should be
 // a child process. Not for user use.
 const char kChildProcess[] = "child-process";
@@ -67,34 +55,4 @@
 // seconds or when the shell exits.
 const char kTraceStartup[] = "trace-startup";
 
-// Specifies a set of mappings to apply when resolving urls. The value is a set
-// of ',' separated mappings, where each mapping consists of a pair of urls
-// giving the to/from url to map. For example, 'a=b,c=d' contains two mappings,
-// the first maps 'a' to 'b' and the second 'c' to 'd'.
-const char kURLMappings[] = "url-mappings";
-
-// Switches valid for the main process (i.e., that the user may pass in).
-const char* kSwitchArray[] = {kV,
-                              kArgsFor,
-                              // |kChildProcess| not for user use.
-                              kContentHandlers,
-                              kDisableCache,
-                              kDontDeleteOnDownload,
-                              kEnableMultiprocess,
-                              kForceInProcess,
-                              kHelp,
-                              kMapOrigin,
-                              kOrigin,
-                              kPredictableAppFilenames,
-                              kTraceStartup,
-                              kURLMappings};
-
-const std::set<std::string> GetAllSwitches() {
-  std::set<std::string> switch_set;
-
-  for (size_t i = 0; i < arraysize(kSwitchArray); ++i)
-    switch_set.insert(kSwitchArray[i]);
-  return switch_set;
-}
-
 }  // namespace switches
diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h
index 52267fec..ff2eb6c 100644
--- a/mojo/shell/switches.h
+++ b/mojo/shell/switches.h
@@ -11,9 +11,7 @@
 namespace switches {
 
 // All switches in alphabetical order. The switches should be documented
-// alongside the definition of their values in the .cc file and, as needed,
-// in mojo_main's Usage() function.
-extern const char kArgsFor[];
+// alongside the definition of their values in the .cc file.
 extern const char kChildProcess[];
 extern const char kContentHandlers[];
 extern const char kDisableCache[];
@@ -25,9 +23,6 @@
 extern const char kOrigin[];
 extern const char kPredictableAppFilenames[];
 extern const char kTraceStartup[];
-extern const char kURLMappings[];
-
-extern const std::set<std::string> GetAllSwitches();
 
 }  // namespace switches
 
diff --git a/mojo/shell/url_resolver_unittest.cc b/mojo/shell/url_resolver_unittest.cc
index f9593216..dd8da75 100644
--- a/mojo/shell/url_resolver_unittest.cc
+++ b/mojo/shell/url_resolver_unittest.cc
@@ -115,7 +115,6 @@
 
   args.clear();
   args.push_back(ARG_LITERAL("mojo_shell"));
-  args.push_back(ARG_LITERAL("--args-for=https://a.org/foo --test"));
   args.push_back(ARG_LITERAL("--map-origin=https://a.org=https://b.org/a"));
   args.push_back(ARG_LITERAL("--map-origin=https://b.org=https://c.org/b"));
   args.push_back(ARG_LITERAL("https://a.org/foo"));
diff --git a/mojo/tools/android_mojo_shell.py b/mojo/tools/android_mojo_shell.py
index d731afe9..e269228 100755
--- a/mojo/tools/android_mojo_shell.py
+++ b/mojo/tools/android_mojo_shell.py
@@ -10,28 +10,10 @@
 from mopy.config import Config
 from mopy import android
 
-USAGE = ("android_mojo_shell.py "
-         "[--args-for=<mojo-app>] "
-         "[--content-handlers=<handlers>] "
-         "[--enable-external-applications] "
-         "[--disable-cache] "
-         "[--enable-multiprocess] "
-         "[--url-mappings=from1=to1,from2=to2] "
-         "[<mojo-app>] "
-         """
-
-A <mojo-app> is a Mojo URL or a Mojo URL and arguments within quotes.
-Example: mojo_shell "mojo:js_standalone test.js".
-<url-lib-path> is searched for shared libraries named by mojo URLs.
-The value of <handlers> is a comma separated list like:
-text/html,mojo:html_viewer,application/javascript,mojo:js_content_handler
-""")
-
-
 def main():
   logging.basicConfig()
 
-  parser = argparse.ArgumentParser(usage=USAGE)
+  parser = argparse.ArgumentParser("Helper for running mojo_shell")
 
   debug_group = parser.add_mutually_exclusive_group()
   debug_group.add_argument('--debug', help='Debug build (default)',
diff --git a/mojo/tools/apptest_runner.py b/mojo/tools/apptest_runner.py
index 901babfa..9ef7b7aa 100755
--- a/mojo/tools/apptest_runner.py
+++ b/mojo/tools/apptest_runner.py
@@ -59,8 +59,7 @@
 
     apptest_result = "Succeeded"
     for fixture in fixtures:
-      args_for_apptest = " ".join(["--args-for=" + apptest,
-                                   "--gtest_filter=" + fixture] + apptest_args)
+      args_for_apptest = " ".join(["--gtest_filter=" + fixture] + apptest_args)
 
       success = RunApptestInShell(mojo_shell_path, apptest,
                                   shell_args + [args_for_apptest])
diff --git a/mojo/tools/gtest.py b/mojo/tools/gtest.py
index c03b62f4..d84c8e5e 100644
--- a/mojo/tools/gtest.py
+++ b/mojo/tools/gtest.py
@@ -70,9 +70,7 @@
   [TestSuite.TestFixture, ... ]
   An empty list is returned on failure, with errors logged.
   """
-  command = [mojo_shell,
-             "--args-for={0} --gtest_list_tests".format(apptest),
-             apptest]
+  command = [mojo_shell, "--gtest_list_tests", apptest]
   try:
     list_output = subprocess.check_output(command, stderr=subprocess.STDOUT)
     _logging.debug("Tests listed:\n%s" % list_output)
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index f424d3d9..fc7fc3e3 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -828,14 +828,21 @@
   return bundle
 
 
+def Archive(filename, from_directory, step_link=True):
+  if buildbot_common.IsSDKBuilder():
+    bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/'
+  else:
+    bucket_path = 'nativeclient-mirror/nacl/nacl_sdk_test/'
+  bucket_path += build_version.ChromeVersion()
+  buildbot_common.Archive(filename, bucket_path, from_directory, step_link)
+
+
 def BuildStepArchiveBundle(name, pepper_ver, chrome_revision, nacl_revision,
                            tarfile):
   buildbot_common.BuildStep('Archive %s' % name)
-  bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/%s' % (
-      build_version.ChromeVersion(),)
   tarname = os.path.basename(tarfile)
   tarfile_dir = os.path.dirname(tarfile)
-  buildbot_common.Archive(tarname, bucket_path, tarfile_dir)
+  Archive(tarname, tarfile_dir)
 
   # generate "manifest snippet" for this archive.
   archive_url = GSTORE + 'nacl_sdk/%s/%s' % (
@@ -847,24 +854,37 @@
   with open(manifest_snippet_file, 'wb') as manifest_snippet_stream:
     manifest_snippet_stream.write(bundle.GetDataAsString())
 
-  buildbot_common.Archive(tarname + '.json', bucket_path, OUT_DIR,
-                          step_link=False)
+  Archive(tarname + '.json', OUT_DIR, step_link=False)
+
+
+def BuildStepBuildPNaClComponent(version, revision):
+  # Sadly revision can go backwords for a given version since when a version
+  # is built from master, revision will be a huge number (in the hundreds of
+  # thousands.  Once the branch happens the revision will reset to zero.
+  # TODO(sbc): figure out how to compensate for this in some way such that
+  # revisions always go forward for a given version.
+  buildbot_common.BuildStep('PNaCl Component')
+  if len(revision) > 4:
+    rev_minor = revision[-4:]
+    rev_major = revision[:-4]
+    version = "0.%s.%s.%s" % (version, rev_major, rev_minor)
+  else:
+    version = "0.%s.0.%s" % (version, revision)
+  buildbot_common.Run(['./make_pnacl_component.sh', version], cwd=SCRIPT_DIR)
+
+
+def BuildStepArchivePNaClComponent():
+  buildbot_common.BuildStep('Archive PNaCl Component')
+  Archive('pnacl_multicrx.zip', OUT_DIR)
 
 
 def BuildStepArchiveSDKTools():
-  # Only push up sdk_tools.tgz and nacl_sdk.zip on the linux buildbot.
-  builder_name = os.getenv('BUILDBOT_BUILDERNAME', '')
-  if builder_name == 'linux-sdk-multi':
-    buildbot_common.BuildStep('Build SDK Tools')
-    build_updater.BuildUpdater(OUT_DIR)
+  buildbot_common.BuildStep('Build SDK Tools')
+  build_updater.BuildUpdater(OUT_DIR)
 
-    buildbot_common.BuildStep('Archive SDK Tools')
-    bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/%s' % (
-        build_version.ChromeVersion(),)
-    buildbot_common.Archive('sdk_tools.tgz', bucket_path, OUT_DIR,
-                            step_link=False)
-    buildbot_common.Archive('nacl_sdk.zip', bucket_path, OUT_DIR,
-                            step_link=False)
+  buildbot_common.BuildStep('Archive SDK Tools')
+  Archive('sdk_tools.tgz', OUT_DIR, step_link=False)
+  Archive('nacl_sdk.zip', OUT_DIR, step_link=False)
 
 
 def BuildStepSyncNaClPorts():
@@ -1123,12 +1143,15 @@
   if options.tar:
     BuildStepTarBundle(pepper_ver, tarfile)
 
-  if options.build_ports and platform == 'linux':
-    ports_tarfile = os.path.join(OUT_DIR, 'naclports.tar.bz2')
-    BuildStepSyncNaClPorts()
-    BuildStepBuildNaClPorts(pepper_ver, pepperdir)
-    if options.tar:
-      BuildStepTarNaClPorts(pepper_ver, ports_tarfile)
+  if platform == 'linux':
+    BuildStepBuildPNaClComponent(pepper_ver, chrome_revision)
+
+    if options.build_ports:
+      ports_tarfile = os.path.join(OUT_DIR, 'naclports.tar.bz2')
+      BuildStepSyncNaClPorts()
+      BuildStepBuildNaClPorts(pepper_ver, pepperdir)
+      if options.tar:
+        BuildStepTarNaClPorts(pepper_ver, ports_tarfile)
 
   if options.build_app_engine and platform == 'linux':
     BuildStepBuildAppEngine(pepperdir, chrome_revision)
@@ -1137,14 +1160,17 @@
     qemudir = os.path.join(NACL_DIR, 'toolchain', 'linux_arm-trusted')
     oshelpers.Copy(['-r', qemudir, pepperdir])
 
-  # Archive on non-trybots.
+  # Archive the results on Google Cloud Storage.
   if options.archive:
     BuildStepArchiveBundle('build', pepper_ver, chrome_revision, nacl_revision,
                            tarfile)
-    if options.build_ports and platform == 'linux':
-      BuildStepArchiveBundle('naclports', pepper_ver, chrome_revision,
-                             nacl_revision, ports_tarfile)
-    BuildStepArchiveSDKTools()
+    # Only archive sdk_tools/naclport/pnacl_component on linux.
+    if platform == 'linux':
+      if options.build_ports:
+        BuildStepArchiveBundle('naclports', pepper_ver, chrome_revision,
+                               nacl_revision, ports_tarfile)
+      BuildStepArchiveSDKTools()
+      BuildStepArchivePNaClComponent()
 
   return 0
 
diff --git a/native_client_sdk/src/build_tools/buildbot_common.py b/native_client_sdk/src/build_tools/buildbot_common.py
index 37aace9..58ebc0a 100644
--- a/native_client_sdk/src/build_tools/buildbot_common.py
+++ b/native_client_sdk/src/build_tools/buildbot_common.py
@@ -37,15 +37,6 @@
   return '-sdk-multi' in bot or '-sdk-bionic-multi' in bot
 
 
-def IsSDKTrybot():
-  """Returns True if this script is running on an SDK trybot.
-
-  False means it is either running on an SDK builder, or a user's machine.
-
-  See IsSDKBuilder above for trybot/buildbot names."""
-  return '_nacl_sdk' in os.getenv('BUILDBOT_BUILDERNAME', '')
-
-
 def ErrorExit(msg):
   """Write and error to stderr, then exit with 1 signaling failure."""
   sys.stderr.write(str(msg) + '\n')
diff --git a/native_client_sdk/src/build_tools/make_pnacl_component.sh b/native_client_sdk/src/build_tools/make_pnacl_component.sh
new file mode 100755
index 0000000..42dc040
--- /dev/null
+++ b/native_client_sdk/src/build_tools/make_pnacl_component.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+# 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.
+
+# This script builds out/pnacl_multicrx.zip for upload to the Chrome
+# Web Store. It runs gyp + ninja once for each architecture and assembles
+# the results along with a manifest file.
+
+# TODO(sbc): rewrite this in python
+
+set -o errexit
+set -o nounset
+
+SCRIPT_DIR="$(cd $(dirname $0) && pwd)"
+CHROME_SRC=$(dirname $(dirname $(dirname ${SCRIPT_DIR})))
+cd ${CHROME_SRC}
+
+run_gyp() {
+  # The original version of the script ran 'gclient runhooks' which run turn
+  # runs gyp_chromium. However its a lot faster and quieter to just run the
+  # gyp file containing the target we need.
+  gyp_dir=ppapi/native_client/src/untrusted/pnacl_support_extension
+  build/gyp_chromium --depth=. $gyp_dir/pnacl_support_extension.gyp
+}
+
+individual_packages() {
+  export GYP_GENERATOR_FLAGS="output_dir=out_pnacl"
+
+  # arm
+  rm -rf out_pnacl/
+  GYP_DEFINES="target_arch=arm" run_gyp
+  ninja -C out_pnacl/Release/ pnacl_support_extension
+  local target_dir=out/pnacl_arm
+  mkdir -p ${target_dir}
+  cp out_pnacl/Release/pnacl/* ${target_dir}/.
+
+  # ia32
+  rm -rf out_pnacl/
+  GYP_DEFINES="target_arch=ia32" run_gyp
+  ninja -C out_pnacl/Release/ pnacl_support_extension
+  target_dir=out/pnacl_x86_32
+  mkdir -p ${target_dir}
+  cp out_pnacl/Release/pnacl/* ${target_dir}/.
+
+  # x64
+  rm -rf out_pnacl/
+  GYP_DEFINES="target_arch=x64" run_gyp
+  ninja -C out_pnacl/Release/ pnacl_support_extension
+  target_dir=out/pnacl_x86_64
+  mkdir -p ${target_dir}
+  cp out_pnacl/Release/pnacl/* ${target_dir}/.
+}
+
+multi_crx() {
+  local version=$1
+  local target_dir=out/pnacl_multicrx
+  mkdir -p ${target_dir}
+  cat > ${target_dir}/manifest.json <<EOF
+{
+  "description": "Portable Native Client Translator Multi-CRX",
+  "name": "PNaCl Translator Multi-CRX",
+  "manifest_version": 2,
+  "minimum_chrome_version": "30.0.0.0",
+  "version": "${version}",
+  "platforms": [
+    {
+      "nacl_arch": "x86-32",
+      "sub_package_path": "_platform_specific/x86_32/"
+    },
+    {
+      "nacl_arch": "x86-64",
+      "sub_package_path": "_platform_specific/x86_64/"
+    },
+    {
+      "nacl_arch": "arm",
+      "sub_package_path": "_platform_specific/arm/"
+    }
+  ]
+}
+EOF
+
+  for arch in x86_32 x86_64 arm; do
+    local sub_dir="${target_dir}/_platform_specific/${arch}"
+    local src_dir="out/pnacl_${arch}"
+    mkdir -p ${sub_dir}
+    cp ${src_dir}/pnacl_public_* ${sub_dir}/.
+  done
+  (cd ${target_dir} && zip -r ../$(basename ${target_dir}).zip .)
+  ls -l ${target_dir}.zip
+  echo "DONE: created ${target_dir}.zip -- upload that!"
+  echo "You can also delete ${target_dir} later (the pre-zipped contents)."
+}
+
+if [ $# != 1 ]; then
+  echo "Usage: $0 <rev_number>"
+  exit 1
+fi
+
+version="$1"
+echo "Buidling pnacl_multicrx.zip version=$version"
+individual_packages
+multi_crx $version
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 05f1106d..9d82fd0 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -863,6 +863,10 @@
       "proxy/mojo_proxy_resolver_impl.h",
     ]
 
+    deps = [
+      ":net_with_v8",
+    ]
+
     public_deps = [
       ":mojo_type_converters",
       ":net",
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
index 32b7192..047a787 100644
--- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
+++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -12,7 +12,7 @@
 import android.util.Log;
 
 import org.chromium.base.CalledByNative;
-import org.chromium.base.CalledByNativeUnchecked;
+import org.chromium.base.annotations.CalledByNativeUnchecked;
 
 import java.net.NetworkInterface;
 import java.net.SocketException;
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 1060509..d799758 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -1234,6 +1234,10 @@
 }
 
 int CookieMonster::DeleteAll(bool sync_to_store) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteAll, sync_to_store="
+                        << sync_to_store;
+
   base::AutoLock autolock(lock_);
 
   int num_deleted = 0;
@@ -1252,6 +1256,9 @@
 
 int CookieMonster::DeleteAllCreatedBetween(const Time& delete_begin,
                                            const Time& delete_end) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteAllCreatedBetween";
+
   base::AutoLock autolock(lock_);
 
   int num_deleted = 0;
@@ -1274,6 +1281,9 @@
 int CookieMonster::DeleteAllCreatedBetweenForHost(const Time delete_begin,
                                                   const Time delete_end,
                                                   const GURL& url) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteAllCreatedBetweenForHost";
+
   base::AutoLock autolock(lock_);
 
   if (!HasCookieableScheme(url))
@@ -1311,6 +1321,9 @@
 }
 
 bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteCanonicalCookie";
+
   base::AutoLock autolock(lock_);
 
   for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain()));
@@ -1380,6 +1393,9 @@
 
 void CookieMonster::DeleteCookie(const GURL& url,
                                  const std::string& cookie_name) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteCookie";
+
   base::AutoLock autolock(lock_);
 
   if (!HasCookieableScheme(url))
@@ -1412,6 +1428,9 @@
 }
 
 int CookieMonster::DeleteSessionCookies() {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::DeleteSessionCookies";
+
   base::AutoLock autolock(lock_);
 
   int num_deleted = 0;
@@ -1484,9 +1503,15 @@
 
 void CookieMonster::OnLoaded(TimeTicks beginning_time,
                              const std::vector<CanonicalCookie*>& cookies) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("457528 CookieMonster::OnLoaded 1"));
   StoreLoadedCookies(cookies);
   histogram_time_blocked_on_load_->AddTime(TimeTicks::Now() - beginning_time);
 
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("457528 CookieMonster::OnLoaded 2"));
   // Invoke the task queue of cookie request.
   InvokeQueue();
 }
@@ -1955,7 +1980,9 @@
     histogram_cookie_deletion_cause_->Add(deletion_cause);
 
   CanonicalCookie* cc = it->second;
-  VLOG(kVlogSetCookies) << "InternalDeleteCookie() cc: " << cc->DebugString();
+  VLOG(kVlogSetCookies) << "InternalDeleteCookie()"
+                        << ", cause:" << deletion_cause
+                        << ", cc: " << cc->DebugString();
 
   if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
       sync_to_store)
@@ -2081,6 +2108,9 @@
 int CookieMonster::GarbageCollectExpired(const Time& current,
                                          const CookieMapItPair& itpair,
                                          CookieItVector* cookie_its) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::GarbageCollectExpired";
+
   if (keep_expired_cookies_)
     return 0;
 
@@ -2106,6 +2136,9 @@
                                              DeletionCause cause,
                                              CookieItVector::iterator it_begin,
                                              CookieItVector::iterator it_end) {
+  // TODO(xiyuan): Remove the log after http://crbug.com/449816.
+  VLOG(kVlogSetCookies) << "CookieMonster::GarbageCollectDeleteRange";
+
   for (CookieItVector::iterator it = it_begin; it != it_end; it++) {
     histogram_evicted_last_access_minutes_->Add(
         (current - (*it)->second->LastAccessDate()).InMinutes());
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 17e9f06..ff5fdf9f 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/compiler_specific.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/load_flags.h"
@@ -126,6 +127,10 @@
 }
 
 void HttpProxyConnectJob::OnIOComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpProxyConnectJob::OnIOComplete"));
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING) {
     NotifyProxyDelegateOfCompletion(rv);
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index dab8bc8..0397aca 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -105,9 +105,6 @@
     request->AttachJob(alternate_job);
     alternate_job->MarkAsAlternate(alternative_service);
 
-    // Never share connection with other jobs for FTP requests.
-    DCHECK(!request_info.url.SchemeIs("ftp"));
-
     job->WaitFor(alternate_job);
     // Make sure to wait until we call WaitFor(), before starting
     // |alternate_job|, otherwise |alternate_job| will not notify |job|
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 78f4eed..9efc4acf 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -436,6 +436,10 @@
 }
 
 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::OnIOComplete"));
   RunLoop(result);
 }
 
diff --git a/net/proxy/proxy_resolver_v8_tracing.cc b/net/proxy/proxy_resolver_v8_tracing.cc
index 3ad166c..230b7caf 100644
--- a/net/proxy/proxy_resolver_v8_tracing.cc
+++ b/net/proxy/proxy_resolver_v8_tracing.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop_proxy.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/cancellation_flag.h"
 #include "base/synchronization/waitable_event.h"
@@ -956,11 +955,6 @@
       net_log_(net_log),
       num_outstanding_callbacks_(0),
       on_load_state_changed_(on_load_state_changed) {
-  // TODO(eroman): Remove once crbug.com/454983 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "454983 ProxyResolverV8Tracing::ProxyResolverV8Tracing"));
-
   DCHECK(host_resolver);
   // Start up the thread.
   thread_.reset(new base::Thread("Proxy resolver"));
diff --git a/net/server/web_socket_encoder.cc b/net/server/web_socket_encoder.cc
index 8e1681c..e35f759 100644
--- a/net/server/web_socket_encoder.cc
+++ b/net/server/web_socket_encoder.cc
@@ -251,8 +251,7 @@
     return;
 
   WebSocketExtensionParser parser;
-  parser.Parse(header_value);
-  if (parser.has_error())
+  if (!parser.Parse(header_value))
     return;
   const std::vector<WebSocketExtension>& extensions = parser.extensions();
   // TODO(tyoshino): Fail if this method is used for parsing a response and
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index 0bc0056b8..f6e9671 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -8,6 +8,7 @@
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
@@ -1127,6 +1128,10 @@
 
 void ClientSocketPoolBaseHelper::InvokeUserCallback(
     ClientSocketHandle* handle) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 ClientSocketPoolBaseHelper::InvokeUserCallback"));
   PendingCallbackMap::iterator it = pending_callback_map_.find(handle);
 
   // Exit if the request has already been cancelled.
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
index ee613ac4..953c0c4 100644
--- a/net/socket/socks_client_socket_pool.cc
+++ b/net/socket/socks_client_socket_pool.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
@@ -76,6 +77,9 @@
 }
 
 void SOCKSConnectJob::OnIOComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("455884 SOCKSConnectJob::OnIOComplete"));
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING)
     NotifyDelegateOfCompletion(rv);  // Deletes |this|
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 29f52a1bc..e6c770c 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -166,6 +166,9 @@
 }
 
 void SSLConnectJob::OnIOComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("455884 SSLConnectJob::OnIOComplete"));
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING)
     NotifyDelegateOfCompletion(rv);  // Deletes |this|.
diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc
index b545162..9b744788 100644
--- a/net/url_request/url_fetcher_impl_unittest.cc
+++ b/net/url_request/url_fetcher_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
@@ -64,24 +65,34 @@
 // Can only be used once.
 class WaitingURLFetcherDelegate : public URLFetcherDelegate {
  public:
-  WaitingURLFetcherDelegate() : fetcher_(nullptr) {}
+  WaitingURLFetcherDelegate() {}
 
-  void StartFetcherAndWait(URLFetcher* fetcher) {
-    EXPECT_FALSE(fetcher_);
-    fetcher_ = fetcher;
+  // Creates a URLFetcher that runs network tasks on the current message loop.
+  void CreateFetcherWithContext(const GURL& url,
+                                URLFetcher::RequestType request_type,
+                                net::URLRequestContext* context) {
+    fetcher_.reset(new URLFetcherImpl(url, request_type, this));
+    fetcher_->SetRequestContext(new TrivialURLRequestContextGetter(
+        context, base::MessageLoopProxy::current()));
+  }
+
+  URLFetcher* fetcher() const { return fetcher_.get(); }
+
+  void StartFetcherAndWait() {
     fetcher_->Start();
     run_loop_.Run();
-    fetcher_ = nullptr;
   }
 
   void OnURLFetchComplete(const URLFetcher* source) override {
-    EXPECT_EQ(fetcher_, source);
+    EXPECT_EQ(fetcher_.get(), source);
     run_loop_.Quit();
   }
 
  private:
-  URLFetcher* fetcher_;
+  scoped_ptr<URLFetcherImpl> fetcher_;
   base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(WaitingURLFetcherDelegate);
 };
 
 class ThrottlingTestURLRequestContext : public TestURLRequestContext {
@@ -350,70 +361,6 @@
   int64 number_of_chunks_added_;
 };
 
-// Version of URLFetcherTest that tests headers.
-class URLFetcherHeadersTest : public URLFetcherTest {
- public:
-  // URLFetcherDelegate:
-  void OnURLFetchComplete(const URLFetcher* source) override;
-};
-
-// Version of URLFetcherTest that tests SocketAddress.
-class URLFetcherSocketAddressTest : public URLFetcherTest {
- public:
-  // URLFetcherDelegate:
-  void OnURLFetchComplete(const URLFetcher* source) override;
-
- protected:
-  std::string expected_host_;
-  uint16 expected_port_;
-};
-
-// Version of URLFetcherTest that tests stopping on a redirect.
-class URLFetcherStopOnRedirectTest : public URLFetcherTest {
- public:
-  URLFetcherStopOnRedirectTest();
-  ~URLFetcherStopOnRedirectTest() override;
-
-  // URLFetcherTest:
-  void CreateFetcher(const GURL& url) override;
-
-  // URLFetcherDelegate:
-  void OnURLFetchComplete(const URLFetcher* source) override;
-
- protected:
-  // The URL we should be redirected to.
-  static const char* kRedirectTarget;
-
-  bool callback_called_;  // Set to true in OnURLFetchComplete().
-};
-
-// Version of URLFetcherTest that tests overload protection.
-class URLFetcherProtectTest : public URLFetcherTest {
- public:
-  // URLFetcherTest:
-  void CreateFetcher(const GURL& url) override;
-
-  // URLFetcherDelegate:
-  void OnURLFetchComplete(const URLFetcher* source) override;
-
- private:
-  Time start_time_;
-};
-
-// Version of URLFetcherTest that tests overload protection, when responses
-// passed through.
-class URLFetcherProtectTestPassedThrough : public URLFetcherTest {
- public:
-  // URLFetcherTest:
-  void CreateFetcher(const GURL& url) override;
-
-  // URLFetcherDelegate:
-  void OnURLFetchComplete(const URLFetcher* source) override;
-
- private:
-  Time start_time_;
-};
-
 // Version of URLFetcherTest that tests bad HTTPS requests.
 class URLFetcherBadHTTPSTest : public URLFetcherTest {
  public:
@@ -619,121 +566,6 @@
   }
 }
 
-void URLFetcherHeadersTest::OnURLFetchComplete(
-    const URLFetcher* source) {
-  std::string header;
-  EXPECT_TRUE(source->GetResponseHeaders()->GetNormalizedHeader("cache-control",
-                                                                &header));
-  EXPECT_EQ("private", header);
-  URLFetcherTest::OnURLFetchComplete(source);
-}
-
-void URLFetcherSocketAddressTest::OnURLFetchComplete(
-    const URLFetcher* source) {
-  EXPECT_EQ("127.0.0.1", source->GetSocketAddress().host());
-  EXPECT_EQ(expected_port_, source->GetSocketAddress().port());
-  URLFetcherTest::OnURLFetchComplete(source);
-}
-
-// static
-const char* URLFetcherStopOnRedirectTest::kRedirectTarget =
-    "http://redirect.target.com";
-
-URLFetcherStopOnRedirectTest::URLFetcherStopOnRedirectTest()
-    : callback_called_(false) {
-}
-
-URLFetcherStopOnRedirectTest::~URLFetcherStopOnRedirectTest() {
-}
-
-void URLFetcherStopOnRedirectTest::CreateFetcher(const GURL& url) {
-  fetcher_ = new URLFetcherImpl(url, URLFetcher::GET, this);
-  fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter(
-      io_message_loop_proxy().get(), request_context()));
-  fetcher_->SetStopOnRedirect(true);
-  fetcher_->Start();
-}
-
-void URLFetcherStopOnRedirectTest::OnURLFetchComplete(
-    const URLFetcher* source) {
-  callback_called_ = true;
-  EXPECT_EQ(GURL(kRedirectTarget), source->GetURL());
-  EXPECT_EQ(URLRequestStatus::CANCELED, source->GetStatus().status());
-  EXPECT_EQ(ERR_ABORTED, source->GetStatus().error());
-  EXPECT_EQ(301, source->GetResponseCode());
-  CleanupAfterFetchComplete();
-}
-
-void URLFetcherProtectTest::CreateFetcher(const GURL& url) {
-  fetcher_ = new URLFetcherImpl(url, URLFetcher::GET, this);
-  fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter(
-      io_message_loop_proxy().get(), request_context()));
-  start_time_ = Time::Now();
-  fetcher_->SetMaxRetriesOn5xx(11);
-  fetcher_->Start();
-}
-
-void URLFetcherProtectTest::OnURLFetchComplete(const URLFetcher* source) {
-  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
-  if (source->GetResponseCode() >= 500) {
-    // Now running ServerUnavailable test.
-    // It takes more than 1 second to finish all 11 requests.
-    EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
-    EXPECT_TRUE(source->GetStatus().is_success());
-    std::string data;
-    EXPECT_TRUE(source->GetResponseAsString(&data));
-    EXPECT_FALSE(data.empty());
-    CleanupAfterFetchComplete();
-  } else {
-    // Now running Overload test.
-    static int count = 0;
-    count++;
-    if (count < 20) {
-      fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter(
-          io_message_loop_proxy().get(), request_context()));
-      fetcher_->Start();
-    } else {
-      // We have already sent 20 requests continuously. And we expect that
-      // it takes more than 1 second due to the overload protection settings.
-      EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
-      URLFetcherTest::OnURLFetchComplete(source);
-    }
-  }
-}
-
-void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) {
-  fetcher_ = new URLFetcherImpl(url, URLFetcher::GET, this);
-  fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter(
-      io_message_loop_proxy().get(), request_context()));
-  fetcher_->SetAutomaticallyRetryOn5xx(false);
-  start_time_ = Time::Now();
-  fetcher_->SetMaxRetriesOn5xx(11);
-  fetcher_->Start();
-}
-
-void URLFetcherProtectTestPassedThrough::OnURLFetchComplete(
-    const URLFetcher* source) {
-  const TimeDelta one_minute = TimeDelta::FromMilliseconds(60000);
-  if (source->GetResponseCode() >= 500) {
-    // Now running ServerUnavailable test.
-    // It should get here on the first attempt, so almost immediately and
-    // *not* to attempt to execute all 11 requests (2.5 minutes).
-    EXPECT_TRUE(Time::Now() - start_time_ < one_minute);
-    EXPECT_TRUE(source->GetStatus().is_success());
-    // Check that suggested back off time is bigger than 0.
-    EXPECT_GT(fetcher_->GetBackoffDelay().InMicroseconds(), 0);
-    std::string data;
-    EXPECT_TRUE(source->GetResponseAsString(&data));
-    EXPECT_FALSE(data.empty());
-  } else {
-    // We should not get here!
-    ADD_FAILURE();
-  }
-
-  CleanupAfterFetchComplete();
-}
-
-
 URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() {
   PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
   cert_dir_ = cert_dir_.AppendASCII("chrome");
@@ -1005,17 +837,16 @@
   const char kUploadData[] = "bobsyeruncle";
 
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo"), URLFetcher::POST,
-                         &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetUploadData("application/x-www-form-urlencoded", kUploadData);
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetUploadData("application/x-www-form-urlencoded",
+                                    kUploadData);
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(200, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(kUploadData, data);
 }
 
@@ -1023,17 +854,16 @@
   const char kUploadData[] = "";
 
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo"), URLFetcher::POST,
-                         &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetUploadData("application/x-www-form-urlencoded", kUploadData);
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetUploadData("application/x-www-form-urlencoded",
+                                    kUploadData);
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(200, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(kUploadData, data);
 }
 
@@ -1041,21 +871,20 @@
   base::FilePath upload_path = GetUploadFileTestPath();
 
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo"), URLFetcher::POST,
-                         &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetUploadFilePath("application/x-www-form-urlencoded", upload_path, 0,
-                            kuint64max, base::MessageLoopProxy::current());
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetUploadFilePath("application/x-www-form-urlencoded",
+                                        upload_path, 0, kuint64max,
+                                        base::MessageLoopProxy::current());
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(200, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
 
   std::string expected;
   ASSERT_TRUE(base::ReadFileToString(upload_path, &expected));
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(expected, data);
 }
 
@@ -1065,61 +894,55 @@
   base::FilePath upload_path = GetUploadFileTestPath();
 
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo"), URLFetcher::POST,
-                         &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetUploadFilePath("application/x-www-form-urlencoded", upload_path,
-                            kRangeStart, kRangeLength,
-                            base::MessageLoopProxy::current());
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetUploadFilePath("application/x-www-form-urlencoded",
+                                        upload_path, kRangeStart, kRangeLength,
+                                        base::MessageLoopProxy::current());
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(200, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
 
   std::string expected;
   ASSERT_TRUE(base::ReadFileToString(upload_path, &expected));
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(expected.substr(kRangeStart, kRangeLength), data);
 }
 
 TEST_F(URLFetcherTest, PostWithUploadStreamFactory) {
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo"), URLFetcher::POST,
-                         &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetUploadStreamFactory(
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetUploadStreamFactory(
       "text/plain",
       base::Bind(&URLFetcherTest::CreateUploadStream, base::Unretained(this)));
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(200, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(kCreateUploadStreamBody, data);
   EXPECT_EQ(1u, num_upload_streams_created());
 }
 
 TEST_F(URLFetcherTest, PostWithUploadStreamFactoryAndRetries) {
   WaitingURLFetcherDelegate delegate;
-  URLFetcherImpl fetcher(test_server_->GetURL("echo?status=500"),
-                         URLFetcher::POST, &delegate);
-  fetcher.SetRequestContext(new TrivialURLRequestContextGetter(
-      request_context(), base::MessageLoopProxy::current()));
-  fetcher.SetAutomaticallyRetryOn5xx(true);
-  fetcher.SetMaxRetriesOn5xx(1);
-  fetcher.SetUploadStreamFactory(
+  delegate.CreateFetcherWithContext(test_server_->GetURL("echo?status=500"),
+                                    URLFetcher::POST, request_context());
+  delegate.fetcher()->SetAutomaticallyRetryOn5xx(true);
+  delegate.fetcher()->SetMaxRetriesOn5xx(1);
+  delegate.fetcher()->SetUploadStreamFactory(
       "text/plain",
       base::Bind(&URLFetcherTest::CreateUploadStream, base::Unretained(this)));
-  delegate.StartFetcherAndWait(&fetcher);
+  delegate.StartFetcherAndWait();
 
-  EXPECT_TRUE(fetcher.GetStatus().is_success());
-  EXPECT_EQ(500, fetcher.GetResponseCode());
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(500, delegate.fetcher()->GetResponseCode());
   std::string data;
-  EXPECT_TRUE(fetcher.GetResponseAsString(&data));
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
   EXPECT_EQ(kCreateUploadStreamBody, data);
   EXPECT_EQ(2u, num_upload_streams_created());
 }
@@ -1155,97 +978,153 @@
   base::MessageLoop::current()->Run();
 }
 
-TEST_F(URLFetcherHeadersTest, Headers) {
-  CreateFetcher(test_server_->GetURL("set-header?cache-control: private"));
-  base::MessageLoop::current()->Run();
-  // The actual tests are in the URLFetcherHeadersTest fixture.
+TEST_F(URLFetcherTest, Headers) {
+  WaitingURLFetcherDelegate delegate;
+  delegate.CreateFetcherWithContext(
+      test_server_->GetURL("set-header?cache-control: private"),
+      URLFetcher::GET, request_context());
+  delegate.StartFetcherAndWait();
+
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
+  std::string header;
+  ASSERT_TRUE(delegate.fetcher()->GetResponseHeaders()->GetNormalizedHeader(
+      "cache-control", &header));
+  EXPECT_EQ("private", header);
 }
 
-TEST_F(URLFetcherSocketAddressTest, SocketAddress) {
-  expected_port_ = test_server_->host_port_pair().port();
+TEST_F(URLFetcherTest, SocketAddress) {
+  WaitingURLFetcherDelegate delegate;
+  delegate.CreateFetcherWithContext(test_server_->GetURL("defaultresponse"),
+                                    URLFetcher::GET, request_context());
+  delegate.StartFetcherAndWait();
 
-  CreateFetcher(test_server_->GetURL("defaultresponse"));
-  base::MessageLoop::current()->Run();
-  // The actual tests are in the URLFetcherSocketAddressTest fixture.
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
+  EXPECT_EQ(test_server_->host_port_pair().port(),
+            delegate.fetcher()->GetSocketAddress().port());
+  EXPECT_EQ(test_server_->host_port_pair().host(),
+            delegate.fetcher()->GetSocketAddress().host());
 }
 
-TEST_F(URLFetcherStopOnRedirectTest, StopOnRedirect) {
-  CreateFetcher(
-      test_server_->GetURL(std::string("server-redirect?") + kRedirectTarget));
-  base::MessageLoop::current()->Run();
-  EXPECT_TRUE(callback_called_);
+TEST_F(URLFetcherTest, StopOnRedirect) {
+  const char kRedirectTarget[] = "http://redirect.target.com";
+
+  WaitingURLFetcherDelegate delegate;
+  delegate.CreateFetcherWithContext(
+      test_server_->GetURL(std::string("server-redirect?") + kRedirectTarget),
+      URLFetcher::GET, request_context());
+  delegate.fetcher()->SetStopOnRedirect(true);
+  delegate.StartFetcherAndWait();
+
+  EXPECT_EQ(GURL(kRedirectTarget), delegate.fetcher()->GetURL());
+  EXPECT_EQ(URLRequestStatus::CANCELED,
+            delegate.fetcher()->GetStatus().status());
+  EXPECT_EQ(ERR_ABORTED, delegate.fetcher()->GetStatus().error());
+  EXPECT_EQ(301, delegate.fetcher()->GetResponseCode());
 }
 
-TEST_F(URLFetcherProtectTest, Overload) {
+TEST_F(URLFetcherTest, ThrottleOnRepeatedFetches) {
+  base::Time start_time = Time::Now();
   GURL url(test_server_->GetURL("defaultresponse"));
 
   // Registers an entry for test url. It only allows 3 requests to be sent
   // in 200 milliseconds.
-  scoped_refptr<URLRequestThrottlerEntry> entry(
-      new URLRequestThrottlerEntry(request_context()->throttler_manager(),
-                                   std::string(),
-                                   200,
-                                   3,
-                                   1,
-                                   2.0,
-                                   0.0,
-                                   256));
+  scoped_refptr<URLRequestThrottlerEntry> entry(new URLRequestThrottlerEntry(
+      request_context()->throttler_manager(), std::string() /* url_id */,
+      200 /* sliding_window_period_ms */, 3 /* max_send_threshold */,
+      1 /* initial_backoff_ms */, 2.0 /* multiply_factor */,
+      0.0 /* jitter_factor */, 256 /* maximum_backoff_ms */));
+
   request_context()->throttler_manager()
       ->OverrideEntryForTests(url, entry.get());
 
-  CreateFetcher(url);
+  for (int i = 0; i < 20; ++i) {
+    WaitingURLFetcherDelegate delegate;
+    delegate.CreateFetcherWithContext(url, URLFetcher::GET, request_context());
+    delegate.StartFetcherAndWait();
 
-  base::MessageLoop::current()->Run();
+    EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+    EXPECT_EQ(200, delegate.fetcher()->GetResponseCode());
+  }
+
+  // 20 requests were sent. Due to throttling, they should have collectively
+  // taken over 1 second.
+  EXPECT_GE(Time::Now() - start_time, base::TimeDelta::FromSeconds(1));
 }
 
-TEST_F(URLFetcherProtectTest, ServerUnavailable) {
+TEST_F(URLFetcherTest, ThrottleOn5xxRetries) {
+  base::Time start_time = Time::Now();
   GURL url(test_server_->GetURL("files/server-unavailable.html"));
 
   // Registers an entry for test url. The backoff time is calculated by:
   //     new_backoff = 2.0 * old_backoff + 0
   // and maximum backoff time is 256 milliseconds.
   // Maximum retries allowed is set to 11.
-  scoped_refptr<URLRequestThrottlerEntry> entry(
-      new URLRequestThrottlerEntry(request_context()->throttler_manager(),
-                                   std::string(),
-                                   200,
-                                   3,
-                                   1,
-                                   2.0,
-                                   0.0,
-                                   256));
+  scoped_refptr<URLRequestThrottlerEntry> entry(new URLRequestThrottlerEntry(
+      request_context()->throttler_manager(), std::string() /* url_id */,
+      200 /* sliding_window_period_ms */, 3 /* max_send_threshold */,
+      1 /* initial_backoff_ms */, 2.0 /* multiply_factor */,
+      0.0 /* jitter_factor */, 256 /* maximum_backoff_ms */));
   request_context()->throttler_manager()
       ->OverrideEntryForTests(url, entry.get());
 
-  CreateFetcher(url);
+  request_context()->throttler_manager()->OverrideEntryForTests(url,
+                                                                entry.get());
 
-  base::MessageLoop::current()->Run();
+  WaitingURLFetcherDelegate delegate;
+  delegate.CreateFetcherWithContext(url, URLFetcher::GET, request_context());
+  delegate.fetcher()->SetAutomaticallyRetryOn5xx(true);
+  delegate.fetcher()->SetMaxRetriesOn5xx(11);
+  delegate.StartFetcherAndWait();
+
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(503, delegate.fetcher()->GetResponseCode());
+  std::string data;
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
+  EXPECT_FALSE(data.empty());
+
+  // The request should have been retried 11 times (12 times including the first
+  // attempt).  Due to throttling, they should have collectively taken over 1
+  // second.
+  EXPECT_GE(Time::Now() - start_time, base::TimeDelta::FromSeconds(1));
 }
 
-TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) {
+// Tests overload protection, when responses passed through.
+TEST_F(URLFetcherTest, ProtectTestPassedThrough) {
+  base::Time start_time = Time::Now();
   GURL url(test_server_->GetURL("files/server-unavailable.html"));
 
   // Registers an entry for test url. The backoff time is calculated by:
   //     new_backoff = 2.0 * old_backoff + 0
   // and maximum backoff time is 150000 milliseconds.
   // Maximum retries allowed is set to 11.
-  scoped_refptr<URLRequestThrottlerEntry> entry(
-      new URLRequestThrottlerEntry(request_context()->throttler_manager(),
-                                   std::string(),
-                                   200,
-                                   3,
-                                   100,
-                                   2.0,
-                                   0.0,
-                                   150000));
+  scoped_refptr<URLRequestThrottlerEntry> entry(new URLRequestThrottlerEntry(
+      request_context()->throttler_manager(), std::string() /* url_id */,
+      200 /* sliding_window_period_ms */, 3 /* max_send_threshold */,
+      10000 /* initial_backoff_ms */, 2.0 /* multiply_factor */,
+      0.0 /* jitter_factor */, 150000 /* maximum_backoff_ms */));
   // Total time if *not* for not doing automatic backoff would be 150s.
   // In reality it should be "as soon as server responds".
   request_context()->throttler_manager()
       ->OverrideEntryForTests(url, entry.get());
 
-  CreateFetcher(url);
+  WaitingURLFetcherDelegate delegate;
+  delegate.CreateFetcherWithContext(url, URLFetcher::GET, request_context());
+  delegate.fetcher()->SetAutomaticallyRetryOn5xx(false);
+  delegate.fetcher()->SetMaxRetriesOn5xx(11);
+  delegate.StartFetcherAndWait();
 
-  base::MessageLoop::current()->Run();
+  EXPECT_TRUE(delegate.fetcher()->GetStatus().is_success());
+  EXPECT_EQ(503, delegate.fetcher()->GetResponseCode());
+  std::string data;
+  ASSERT_TRUE(delegate.fetcher()->GetResponseAsString(&data));
+  EXPECT_FALSE(data.empty());
+  EXPECT_GT(delegate.fetcher()->GetBackoffDelay().InMicroseconds(), 0);
+
+  // The request should not have been retried at all.  If it had attempted all
+  // 11 retries, that should have taken 2.5 minutes.
+  EXPECT_TRUE(Time::Now() - start_time < TimeDelta::FromMinutes(1));
 }
 
 TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) {
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc
index a4a7d7b5..7bb34a3 100644
--- a/net/websockets/websocket_basic_handshake_stream.cc
+++ b/net/websockets/websocket_basic_handshake_stream.cc
@@ -315,8 +315,7 @@
   while (headers->EnumerateHeader(&state, websockets::kSecWebSocketExtensions,
                                   &header_value)) {
     WebSocketExtensionParser parser;
-    parser.Parse(header_value);
-    if (parser.has_error()) {
+    if (!parser.Parse(header_value)) {
       // TODO(yhirano) Set appropriate failure message.
       *failure_message =
           "'Sec-WebSocket-Extensions' header value is "
diff --git a/net/websockets/websocket_extension_parser.cc b/net/websockets/websocket_extension_parser.cc
index 109d330..b737d5e 100644
--- a/net/websockets/websocket_extension_parser.cc
+++ b/net/websockets/websocket_extension_parser.cc
@@ -12,100 +12,102 @@
 
 WebSocketExtensionParser::~WebSocketExtensionParser() {}
 
-void WebSocketExtensionParser::Parse(const char* data, size_t size) {
+bool WebSocketExtensionParser::Parse(const char* data, size_t size) {
   current_ = data;
   end_ = data + size;
-  has_error_ = false;
   extensions_.clear();
 
+  bool failed = false;
+
   while (true) {
     WebSocketExtension extension;
-    ConsumeExtension(&extension);
-    if (has_error_)
+    if (!ConsumeExtension(&extension)) {
+      failed = true;
       break;
+    }
     extensions_.push_back(extension);
 
     ConsumeSpaces();
-    DCHECK(!has_error_);
 
     if (!ConsumeIfMatch(',')) {
       break;
     }
   }
 
-  has_error_ = has_error_ || current_ != end_;
-  if (has_error_)
-    extensions_.clear();
+  if (!failed && current_ == end_)
+    return true;
+
+  extensions_.clear();
+  return false;
 }
 
-void WebSocketExtensionParser::Consume(char c) {
-  DCHECK(!has_error_);
+bool WebSocketExtensionParser::Consume(char c) {
   ConsumeSpaces();
-  DCHECK(!has_error_);
   if (current_ == end_ || c != current_[0]) {
-    has_error_ = true;
-    return;
+    return false;
   }
   ++current_;
+  return true;
 }
 
-void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) {
-  DCHECK(!has_error_);
+bool WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) {
   base::StringPiece name;
-  ConsumeToken(&name);
-  if (has_error_) return;
+  if (!ConsumeToken(&name))
+    return false;
   *extension = WebSocketExtension(name.as_string());
 
   while (ConsumeIfMatch(';')) {
     WebSocketExtension::Parameter parameter((std::string()));
-    ConsumeExtensionParameter(&parameter);
-    if (has_error_) return;
+    if (!ConsumeExtensionParameter(&parameter))
+      return false;
     extension->Add(parameter);
   }
+
+  return true;
 }
 
-void WebSocketExtensionParser::ConsumeExtensionParameter(
+bool WebSocketExtensionParser::ConsumeExtensionParameter(
     WebSocketExtension::Parameter* parameter) {
-  DCHECK(!has_error_);
   base::StringPiece name, value;
   std::string value_string;
 
-  ConsumeToken(&name);
-  if (has_error_) return;
+  if (!ConsumeToken(&name))
+    return false;
+
   if (!ConsumeIfMatch('=')) {
     *parameter = WebSocketExtension::Parameter(name.as_string());
-    return;
+    return true;
   }
 
   if (Lookahead('\"')) {
-    ConsumeQuotedToken(&value_string);
+    if (!ConsumeQuotedToken(&value_string))
+      return false;
   } else {
-    ConsumeToken(&value);
+    if (!ConsumeToken(&value))
+      return false;
     value_string = value.as_string();
   }
-  if (has_error_) return;
   *parameter = WebSocketExtension::Parameter(name.as_string(), value_string);
+  return true;
 }
 
-void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) {
-  DCHECK(!has_error_);
+bool WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) {
   ConsumeSpaces();
-  DCHECK(!has_error_);
   const char* head = current_;
   while (current_ < end_ &&
          !IsControl(current_[0]) && !IsSeparator(current_[0]))
     ++current_;
   if (current_ == head) {
-    has_error_ = true;
-    return;
+    return false;
   }
   *token = base::StringPiece(head, current_ - head);
+  return true;
 }
 
-void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) {
-  DCHECK(!has_error_);
-  Consume('"');
-  if (has_error_) return;
+bool WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) {
+  if (!Consume('"'))
+    return false;
+
   *token = "";
   while (current_ < end_ && !IsControl(current_[0])) {
     if (UnconsumedBytes() >= 2 && current_[0] == '\\') {
@@ -121,41 +123,34 @@
     }
   }
   // We can't use Consume here because we don't want to consume spaces.
-  if (current_ < end_ && current_[0] == '"')
-    ++current_;
-  else
-    has_error_ = true;
-  has_error_ = has_error_ || token->empty();
+  if (current_ >= end_ || current_[0] != '"')
+    return false;
+
+  ++current_;
+
+  return !token->empty();
 }
 
 void WebSocketExtensionParser::ConsumeSpaces() {
-  DCHECK(!has_error_);
   while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t'))
     ++current_;
   return;
 }
 
 bool WebSocketExtensionParser::Lookahead(char c) {
-  DCHECK(!has_error_);
   const char* head = current_;
-
-  Consume(c);
-  bool result = !has_error_;
+  bool result = Consume(c);
   current_ = head;
-  has_error_ = false;
   return result;
 }
 
 bool WebSocketExtensionParser::ConsumeIfMatch(char c) {
-  DCHECK(!has_error_);
   const char* head = current_;
-
-  Consume(c);
-  if (has_error_) {
+  if (!Consume(c)) {
     current_ = head;
-    has_error_ = false;
     return false;
   }
+
   return true;
 }
 
diff --git a/net/websockets/websocket_extension_parser.h b/net/websockets/websocket_extension_parser.h
index 6b2e4dc..007040de 100644
--- a/net/websockets/websocket_extension_parser.h
+++ b/net/websockets/websocket_extension_parser.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/compiler_specific.h"
 #include "base/strings/string_piece.h"
 #include "net/base/net_export.h"
 #include "net/websockets/websocket_extension.h"
@@ -23,28 +24,28 @@
   //
   // There must be no newline characters in the input. LWS-concatenation must
   // have already been done before calling this method.
-  void Parse(const char* data, size_t size);
-  void Parse(const std::string& data) {
-    Parse(data.data(), data.size());
+  //
+  // Returns true if the method was successful (no syntax error was found).
+  bool Parse(const char* data, size_t size);
+  bool Parse(const std::string& data) {
+    return Parse(data.data(), data.size());
   }
 
-  // Returns true if the last Parse() method call encountered any syntax error.
-  bool has_error() const { return has_error_; }
   // Returns the result of the last Parse() method call.
   const std::vector<WebSocketExtension>& extensions() const {
     return extensions_;
   }
 
  private:
-  // TODO(tyoshino): Make the following methods return success/fail.
-  void Consume(char c);
-  void ConsumeExtension(WebSocketExtension* extension);
-  void ConsumeExtensionParameter(WebSocketExtension::Parameter* parameter);
-  void ConsumeToken(base::StringPiece* token);
-  void ConsumeQuotedToken(std::string* token);
+  WARN_UNUSED_RESULT bool Consume(char c);
+  WARN_UNUSED_RESULT bool ConsumeExtension(WebSocketExtension* extension);
+  WARN_UNUSED_RESULT bool ConsumeExtensionParameter(
+      WebSocketExtension::Parameter* parameter);
+  WARN_UNUSED_RESULT bool ConsumeToken(base::StringPiece* token);
+  WARN_UNUSED_RESULT bool ConsumeQuotedToken(std::string* token);
   void ConsumeSpaces();
-  bool Lookahead(char c);
-  bool ConsumeIfMatch(char c);
+  WARN_UNUSED_RESULT bool Lookahead(char c);
+  WARN_UNUSED_RESULT bool ConsumeIfMatch(char c);
   size_t UnconsumedBytes() const { return end_ - current_; }
 
   static bool IsControl(char c);
@@ -54,7 +55,6 @@
   const char* current_;
   // The pointer of the end of the input string.
   const char* end_;
-  bool has_error_;
   std::vector<WebSocketExtension> extensions_;
 
   DISALLOW_COPY_AND_ASSIGN(WebSocketExtensionParser);
diff --git a/net/websockets/websocket_extension_parser_test.cc b/net/websockets/websocket_extension_parser_test.cc
index 616f8ec..b0a2be9 100644
--- a/net/websockets/websocket_extension_parser_test.cc
+++ b/net/websockets/websocket_extension_parser_test.cc
@@ -15,9 +15,8 @@
 
 TEST(WebSocketExtensionParserTest, ParseEmpty) {
   WebSocketExtensionParser parser;
-  parser.Parse("", 0);
+  EXPECT_FALSE(parser.Parse("", 0));
 
-  EXPECT_TRUE(parser.has_error());
   EXPECT_EQ(0U, parser.extensions().size());
 }
 
@@ -25,9 +24,8 @@
   WebSocketExtensionParser parser;
   WebSocketExtension expected("foo");
 
-  parser.Parse("foo");
+  EXPECT_TRUE(parser.Parse("foo"));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
@@ -36,17 +34,14 @@
   WebSocketExtensionParser parser;
   WebSocketExtension expected("foo");
 
-  parser.Parse("foo");
-  ASSERT_FALSE(parser.has_error());
+  EXPECT_TRUE(parser.Parse("foo"));
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 
-  parser.Parse("");
-  EXPECT_TRUE(parser.has_error());
+  EXPECT_FALSE(parser.Parse(""));
   EXPECT_EQ(0U, parser.extensions().size());
 
-  parser.Parse("foo");
-  ASSERT_FALSE(parser.has_error());
+  EXPECT_TRUE(parser.Parse("foo"));
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
@@ -56,9 +51,8 @@
   WebSocketExtension expected("foo");
   expected.Add(WebSocketExtension::Parameter("bar"));
 
-  parser.Parse("\tfoo ; bar");
+  EXPECT_TRUE(parser.Parse("\tfoo ; bar"));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
@@ -68,9 +62,8 @@
   WebSocketExtension expected("foo");
   expected.Add(WebSocketExtension::Parameter("bar", "baz"));
 
-  parser.Parse("foo ; bar= baz\t");
+  EXPECT_TRUE(parser.Parse("foo ; bar= baz\t"));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
@@ -81,9 +74,8 @@
   expected.Add(WebSocketExtension::Parameter("bar", "baz"));
   expected.Add(WebSocketExtension::Parameter("hoge", "fuga"));
 
-  parser.Parse("foo ; bar= baz;\t \thoge\t\t=fuga");
+  EXPECT_TRUE(parser.Parse("foo ; bar= baz;\t \thoge\t\t=fuga"));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
@@ -97,9 +89,8 @@
   WebSocketExtension expected1("bar");
   expected1.Add(WebSocketExtension::Parameter("beta", "y"));
 
-  parser.Parse(" foo ; alpha = x , bar ; beta = y ");
+  EXPECT_TRUE(parser.Parse(" foo ; alpha = x , bar ; beta = y "));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(2U, parser.extensions().size());
 
   EXPECT_TRUE(expected0.Equals(parser.extensions()[0]));
@@ -113,6 +104,7 @@
       "foo,",                 // second extension is incomplete (empty)
       "foo , ",               // second extension is incomplete (space)
       "foo,;",                // second extension is incomplete (semicolon)
+      "foo;, bar",            // first extension is incomplete
       "fo\ao",                // control in extension name
       "fo\x01o",              // control in extension name
       "fo<o",                 // separator in extension name
@@ -147,8 +139,7 @@
 
   for (size_t i = 0; i < arraysize(patterns); ++i) {
     WebSocketExtensionParser parser;
-    parser.Parse(patterns[i]);
-    EXPECT_TRUE(parser.has_error());
+    EXPECT_FALSE(parser.Parse(patterns[i]));
     EXPECT_EQ(0U, parser.extensions().size());
   }
 }
@@ -158,9 +149,8 @@
   WebSocketExtension expected("foo");
   expected.Add(WebSocketExtension::Parameter("bar", "baz"));
 
-  parser.Parse("foo; bar = \"ba\\z\" ");
+  EXPECT_TRUE(parser.Parse("foo; bar = \"ba\\z\" "));
 
-  ASSERT_FALSE(parser.has_error());
   ASSERT_EQ(1U, parser.extensions().size());
   EXPECT_TRUE(expected.Equals(parser.extensions()[0]));
 }
diff --git a/pdf/paint_manager.cc b/pdf/paint_manager.cc
index f452b37..925e1a0 100644
--- a/pdf/paint_manager.cc
+++ b/pdf/paint_manager.cc
@@ -97,8 +97,8 @@
 }
 
 void PaintManager::Invalidate() {
-  // You must call SetSize before using.
-  DCHECK(!graphics_.is_null() || has_pending_resize_);
+  if (graphics_.is_null() && !has_pending_resize_)
+    return;
 
   EnsureCallbackPending();
   aggregator_.InvalidateRect(pp::Rect(GetEffectiveSize()));
@@ -107,8 +107,8 @@
 void PaintManager::InvalidateRect(const pp::Rect& rect) {
   DCHECK(!in_paint_);
 
-  // You must call SetSize before using.
-  DCHECK(!graphics_.is_null() || has_pending_resize_);
+  if (graphics_.is_null() && !has_pending_resize_)
+    return;
 
   // Clip the rect to the device area.
   pp::Rect clipped_rect = rect.Intersect(pp::Rect(GetEffectiveSize()));
@@ -123,8 +123,8 @@
                               const pp::Point& amount) {
   DCHECK(!in_paint_);
 
-  // You must call SetSize before using.
-  DCHECK(!graphics_.is_null() || has_pending_resize_);
+  if (graphics_.is_null() && !has_pending_resize_)
+    return;
 
   EnsureCallbackPending();
 
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index 823c55e..15372f8 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -289,6 +289,7 @@
       'webapp/crd/js/crd_main.js',
       'webapp/crd/js/activity.js',
       'webapp/crd/js/desktop_remoting.js',
+      'webapp/crd/js/desktop_remoting_activity.js',
       'webapp/crd/js/it2me_activity.js',
       'webapp/crd/js/me2me_activity.js',
     ],
diff --git a/remoting/webapp/app_remoting/js/app_remoting.js b/remoting/webapp/app_remoting/js/app_remoting.js
index ed04bd1..e1749d8 100644
--- a/remoting/webapp/app_remoting/js/app_remoting.js
+++ b/remoting/webapp/app_remoting/js/app_remoting.js
@@ -30,10 +30,11 @@
  * @constructor
  * @implements {remoting.ApplicationInterface}
  * @implements {remoting.ProtocolExtension}
+ * @implements {remoting.ClientSession.EventHandler}
  * @extends {remoting.Application}
  */
 remoting.AppRemoting = function(appCapabilities) {
-  base.inherits(this, remoting.Application, appCapabilities);
+  base.inherits(this, remoting.Application);
 
   /** @private {remoting.ApplicationContextMenu} */
   this.contextMenu_ = null;
@@ -52,6 +53,11 @@
 
   /** @private */
   this.supportsGoogleDrive_ = false;
+
+  /** @private */
+  this.sessionConnector_ = remoting.SessionConnector.factory.createConnector(
+      document.getElementById('client-container'),
+      appCapabilities, this);
 };
 
 /**
@@ -92,7 +98,7 @@
  * @override {remoting.ApplicationInterface}
  */
 remoting.AppRemoting.prototype.signInFailed_ = function(error) {
-  this.onError_(error);
+  this.onError(error);
 };
 
 /**
@@ -172,31 +178,13 @@
             remoting.Application.Mode.APP_REMOTING, host,
             new remoting.CredentialsProvider(
                 {fetchThirdPartyToken: fetchThirdPartyToken}));
-
       } else if (response && response.status == 'pending') {
-        that.onError_(new remoting.Error(
+        that.onError(new remoting.Error(
             remoting.Error.Tag.SERVICE_UNAVAILABLE));
       }
     } else {
       console.error('Invalid "runApplication" response from server.');
-      // TODO(garykac) Start using remoting.Error.fromHttpStatus once it has
-      // been updated to properly report 'unknown' errors (rather than
-      // reporting them as AUTHENTICATION_FAILED).
-      if (xhrResponse.status == 0) {
-        that.onError_(new remoting.Error(
-            remoting.Error.Tag.NETWORK_FAILURE));
-      } else if (xhrResponse.status == 401) {
-        that.onError_(new remoting.Error(
-            remoting.Error.Tag.AUTHENTICATION_FAILED));
-      } else if (xhrResponse.status == 403) {
-        that.onError_(new remoting.Error(
-            remoting.Error.Tag.APP_NOT_AUTHORIZED));
-      } else if (xhrResponse.status == 502 || xhrResponse.status == 503) {
-        that.onError_(new remoting.Error(
-            remoting.Error.Tag.SERVICE_UNAVAILABLE));
-      } else {
-        that.onError_(remoting.Error.unexpected());
-      }
+      that.onError(remoting.Error.fromHttpStatus(xhrResponse.status));
     }
   };
 
@@ -217,11 +205,8 @@
 
 /**
  * @param {remoting.ConnectionInfo} connectionInfo
- * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.onConnected_ = function(connectionInfo) {
-  this.initSession_(connectionInfo);
-
+remoting.AppRemoting.prototype.onConnected = function(connectionInfo) {
   this.supportsGoogleDrive_ = connectionInfo.session().hasCapability(
       remoting.ClientSession.Capability.GOOGLE_DRIVE);
 
@@ -237,10 +222,7 @@
   }
 };
 
-/**
- * @override {remoting.ApplicationInterface}
- */
-remoting.AppRemoting.prototype.onDisconnected_ = function() {
+remoting.AppRemoting.prototype.onDisconnected = function() {
   base.dispose(this.connectedView_);
   this.connectedView_ = null;
   this.stopExtension_();
@@ -249,17 +231,15 @@
 
 /**
  * @param {!remoting.Error} error
- * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.onConnectionFailed_ = function(error) {
-  this.onError_(error);
+remoting.AppRemoting.prototype.onConnectionFailed = function(error) {
+  this.onError(error);
 };
 
 /**
  * @param {!remoting.Error} error The error to be localized and displayed.
- * @override {remoting.ApplicationInterface}
  */
-remoting.AppRemoting.prototype.onError_ = function(error) {
+remoting.AppRemoting.prototype.onError = function(error) {
   console.error('Connection failed: ' + error.toString());
   remoting.LoadingWindow.close();
   remoting.MessageWindow.showErrorMessage(
diff --git a/remoting/webapp/base/js/application.js b/remoting/webapp/base/js/application.js
index 4679ab8..4e79fccb 100644
--- a/remoting/webapp/base/js/application.js
+++ b/remoting/webapp/base/js/application.js
@@ -13,38 +13,16 @@
 var remoting = remoting || {};
 
 /**
- * @param {Array<string>} appCapabilities Array of application capabilities.
  * @constructor
- * @implements {remoting.ApplicationInterface}
  */
-remoting.Application = function(appCapabilities) {
+remoting.Application = function() {
   // Create global factories.
   remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory();
   remoting.SessionConnector.factory =
       new remoting.DefaultSessionConnectorFactory();
 
-  /** @private {Array<string>} */
-  this.appCapabilities_ = [
-    remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION,
-    remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS,
-    remoting.ClientSession.Capability.VIDEO_RECORDER
-  ];
-  // Append the app-specific capabilities.
-  this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities);
-
-  /** @protected {remoting.SessionConnector} */
-  this.sessionConnector_ = remoting.SessionConnector.factory.createConnector(
-      document.getElementById('client-container'),
-      this.onConnected_.bind(this),
-      this.onError_.bind(this),
-      this.onConnectionFailed_.bind(this),
-      this.appCapabilities_);
-
   /** @protected {remoting.Application.Mode} */
   this.connectionMode_ = remoting.Application.Mode.ME2ME;
-
-  /** @private {base.Disposable} */
-  this.sessionConnectedHooks_ = null;
 };
 
 /**
@@ -62,13 +40,6 @@
 };
 
 /**
- * @return {remoting.SessionConnector} The session connector.
- */
-remoting.Application.prototype.getSessionConnector = function() {
-  return this.sessionConnector_;
-};
-
-/**
  * Get the connection mode (Me2Me, IT2Me or AppRemoting).
  *
  * @return {remoting.Application.Mode}
@@ -86,15 +57,6 @@
   this.connectionMode_ = mode;
 };
 
-/**
- * @param {remoting.ClientSession.Capability} capability
- * @return {boolean}
- */
-remoting.Application.prototype.hasCapability = function(capability) {
-  var capabilities = this.appCapabilities_;
-  return capabilities.indexOf(capability) != -1;
-};
-
 /* Disconnect the remoting client. */
 remoting.Application.prototype.disconnect = function() {
   if (remoting.clientSession) {
@@ -132,81 +94,18 @@
   this.initApplication_();
 
   var that = this;
-  remoting.identity.getToken().
-    then(this.startApplication_.bind(this)).
-    catch(remoting.Error.handler(
-      function(/** !remoting.Error */ error) {
-        if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
-          that.exitApplication_();
-        } else {
-          that.signInFailed_(error);
-        }
-      }
-    )
-  );
+  remoting.identity.getToken().then(
+    this.startApplication_.bind(this)
+  ).catch(function(/** !remoting.Error */ error) {
+    if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
+      that.exitApplication_();
+    } else {
+      that.signInFailed_(error);
+    }
+  });
 };
 
 /**
- * Called when a new session has been connected.
- *
- * @param {remoting.ConnectionInfo} connectionInfo
- * @return {void} Nothing.
- * @protected
- */
-remoting.Application.prototype.initSession_ = function(connectionInfo) {
-  this.sessionConnectedHooks_ = new base.Disposables(
-    new base.EventHook(connectionInfo.session(), 'stateChanged',
-                       this.onSessionFinished_.bind(this)),
-    new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000)
-  );
-};
-
-/**
- * Callback function called when the state of the client plugin changes. The
- * current and previous states are available via the |state| member variable.
- *
- * @param {remoting.ClientSession.StateEvent=} state
- * @private
- */
-remoting.Application.prototype.onSessionFinished_ = function(state) {
-  switch (state.current) {
-    case remoting.ClientSession.State.CLOSED:
-      console.log('Connection closed by host');
-      this.onDisconnected_();
-      break;
-    case remoting.ClientSession.State.FAILED:
-      var error = remoting.clientSession.getError();
-      console.error('Client plugin reported connection failed: ' +
-                    error.toString());
-      if (error === null) {
-        error = remoting.Error.unexpected();
-      }
-      this.onError_(error);
-      break;
-
-    default:
-      console.error('Unexpected client plugin state: ' + state.current);
-      // This should only happen if the web-app and client plugin get out of
-      // sync, so MISSING_PLUGIN is a suitable error.
-      this.onError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
-      break;
-  }
-
-  base.dispose(this.sessionConnectedHooks_);
-  this.sessionConnectedHooks_= null;
-  this.sessionConnector_.closeSession();
-};
-
-/** @private */
-remoting.Application.prototype.updateStatistics_ = function() {
-  var perfstats = remoting.clientSession.getPerfStats();
-  remoting.stats.update(perfstats);
-  remoting.clientSession.logStatistics(perfstats);
-};
-
-
-/*
- * remoting.ApplicationInterface
  * These functions must be overridden in the subclass.
  */
 
@@ -242,36 +141,6 @@
 };
 
 /**
- * @param {remoting.ConnectionInfo} connectionInfo
- * @protected
- */
-remoting.Application.prototype.onConnected_ = function(connectionInfo) {
-  base.debug.assert(false, "Subclass must override");
-};
-
-/** @protected */
-remoting.Application.prototype.onDisconnected_ = function() {
-  base.debug.assert(false, "Subclass must override");
-};
-
-/**
- * @param {!remoting.Error} error
- * @protected
- */
-remoting.Application.prototype.onConnectionFailed_ = function(error) {
-  base.debug.assert(false, "Subclass must override");
-};
-
-/**
- * @param {!remoting.Error} error The error to be localized and displayed.
- * @protected
- */
-remoting.Application.prototype.onError_ = function(error) {
-  base.debug.assert(false, "Subclass must override");
-};
-
-
-/**
  * The interface specifies the methods that a subclass of remoting.Application
  * is required implement to override the default behavior.
  *
@@ -316,34 +185,5 @@
  */
 remoting.ApplicationInterface.prototype.exitApplication_ = function() {};
 
-/**
- * Called when a new session has been connected.
- *
- * @param {remoting.ConnectionInfo} connectionInfo
- */
-remoting.ApplicationInterface.prototype.onConnected_ =
-    function(connectionInfo) {};
-
-/**
- * Called when the current session has been disconnected.
- */
-remoting.ApplicationInterface.prototype.onDisconnected_ = function() {};
-
-/**
- * Called when the current session's connection has failed.
- *
- * @param {!remoting.Error} error
- */
-remoting.ApplicationInterface.prototype.onConnectionFailed_ =
-    function(error) {};
-
-/**
- * Called when an error needs to be displayed to the user.
- *
- * @param {!remoting.Error} errorTag The error to be localized and displayed.
- */
-remoting.ApplicationInterface.prototype.onError_ = function(errorTag) {};
-
-
 /** @type {remoting.Application} */
 remoting.app = null;
diff --git a/remoting/webapp/crd/js/client_plugin_impl.js b/remoting/webapp/crd/js/client_plugin_impl.js
index ff8d8ad..c2ba6c0 100644
--- a/remoting/webapp/crd/js/client_plugin_impl.js
+++ b/remoting/webapp/crd/js/client_plugin_impl.js
@@ -34,6 +34,9 @@
  */
 remoting.ClientPluginImpl = function(container,
                                      requiredCapabilities) {
+  // TODO(kelvinp): Hack to remove all plugin elements as our current code does
+  // not handle connection cancellation properly.
+  container.innerText = '';
   this.plugin_ = remoting.ClientPluginImpl.createPluginElement_();
   this.plugin_.id = 'session-client-plugin';
   container.appendChild(this.plugin_);
diff --git a/remoting/webapp/crd/js/client_session.js b/remoting/webapp/crd/js/client_session.js
index 2e90e97..43a99be 100644
--- a/remoting/webapp/crd/js/client_session.js
+++ b/remoting/webapp/crd/js/client_session.js
@@ -71,6 +71,9 @@
   this.plugin_ = plugin;
   plugin.setConnectionEventHandler(this);
 
+  /** @private  */
+  this.connectedDisposables_ = new base.Disposables();
+
   this.defineEvents(Object.keys(remoting.ClientSession.Events));
 };
 
@@ -309,6 +312,8 @@
  * @return {void} Nothing.
  */
 remoting.ClientSession.prototype.dispose = function() {
+  base.dispose(this.connectedDisposables_);
+  this.connectedDisposables_ = null;
   this.plugin_ = null;
 };
 
@@ -491,6 +496,15 @@
   this.capabilities_ = capabilities;
 };
 
+/** @return {boolean} */
+remoting.ClientSession.prototype.isFinished = function() {
+  var finishedStates = [
+    remoting.ClientSession.State.CLOSED,
+    remoting.ClientSession.State.FAILED,
+    remoting.ClientSession.State.CONNECTION_DROPPED
+  ];
+  return finishedStates.indexOf(this.getState()) !== -1;
+};
 /**
  * @param {remoting.ClientSession.State} newState The new state for the session.
  * @return {void} Nothing.
@@ -498,25 +512,17 @@
  */
 remoting.ClientSession.prototype.setState_ = function(newState) {
   var oldState = this.state_;
-  this.state_ = newState;
-  var state = this.state_;
-  if (oldState == remoting.ClientSession.State.CONNECTING ||
-      oldState == remoting.ClientSession.State.AUTHENTICATED) {
-    if (this.state_ == remoting.ClientSession.State.CLOSED) {
-      state = remoting.ClientSession.State.CONNECTION_CANCELED;
-    } else if (this.state_ == remoting.ClientSession.State.FAILED &&
-        this.error_.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
-        !this.logHostOfflineErrors_) {
-      // The application requested host-offline errors to be suppressed, for
-      // example, because this connection attempt is using a cached host JID.
-      console.log('Suppressing host-offline error.');
-      state = remoting.ClientSession.State.CONNECTION_CANCELED;
-    }
-  } else if (oldState == remoting.ClientSession.State.CONNECTED &&
-             this.state_ == remoting.ClientSession.State.FAILED) {
-    state = remoting.ClientSession.State.CONNECTION_DROPPED;
+  this.state_ = this.translateState_(oldState, newState);
+
+  if (newState == remoting.ClientSession.State.CONNECTED) {
+    this.connectedDisposables_.add(
+        new base.RepeatingTimer(this.reportStatistics.bind(this), 1000));
+  } else if (this.isFinished()) {
+    base.dispose(this.connectedDisposables_);
+    this.connectedDisposables_ = null;
   }
-  this.logToServer.logClientSessionStateChange(state, this.error_);
+
+  this.logToServer.logClientSessionStateChange(this.state_, this.error_);
 
   this.raiseEvent(remoting.ClientSession.Events.stateChanged,
     new remoting.ClientSession.StateEvent(newState, oldState)
@@ -524,21 +530,33 @@
 };
 
 /**
- * Returns an associative array with a set of stats for this connection.
- *
- * @return {remoting.ClientSession.PerfStats} The connection statistics.
+ * @param {remoting.ClientSession.State} previous
+ * @param {remoting.ClientSession.State} current
+ * @return {remoting.ClientSession.State}
+ * @private
  */
-remoting.ClientSession.prototype.getPerfStats = function() {
-  return this.plugin_.getPerfStats();
+remoting.ClientSession.prototype.translateState_ = function(previous, current) {
+  var State = remoting.ClientSession.State;
+  if (previous == State.CONNECTING || previous == State.AUTHENTICATED) {
+    if (current == State.CLOSED) {
+      return remoting.ClientSession.State.CONNECTION_CANCELED;
+    } else if (current == State.FAILED &&
+        this.error_.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
+        !this.logHostOfflineErrors_) {
+      // The application requested host-offline errors to be suppressed, for
+      // example, because this connection attempt is using a cached host JID.
+      console.log('Suppressing host-offline error.');
+      return State.CONNECTION_CANCELED;
+    }
+  } else if (previous == State.CONNECTED && current == State.FAILED) {
+    return State.CONNECTION_DROPPED;
+  }
+  return current;
 };
 
-/**
- * Logs statistics.
- *
- * @param {remoting.ClientSession.PerfStats} stats
- */
-remoting.ClientSession.prototype.logStatistics = function(stats) {
-  this.logToServer.logStatistics(stats);
+/** @private */
+remoting.ClientSession.prototype.reportStatistics = function() {
+  this.logToServer.logStatistics(this.plugin_.getPerfStats());
 };
 
 /**
diff --git a/remoting/webapp/crd/js/crd_main.js b/remoting/webapp/crd/js/crd_main.js
index d2a4f856..cdfaecf 100644
--- a/remoting/webapp/crd/js/crd_main.js
+++ b/remoting/webapp/crd/js/crd_main.js
@@ -133,7 +133,8 @@
   };
 
   remoting.hostController.hasFeature(
-      remoting.HostController.Feature.PAIRING_REGISTRY, onHasFeatureResponse);
+      remoting.HostController.Feature.PAIRING_REGISTRY).
+      then(onHasFeatureResponse);
   remoting.hostController.getLocalHostState(onHostState);
 };
 
@@ -177,7 +178,7 @@
 
 
 remoting.startDesktopRemoting = function() {
-  remoting.app = new remoting.DesktopRemoting(remoting.app_capabilities());
+  remoting.app = new remoting.DesktopRemoting();
   remoting.app.start();
 };
 
diff --git a/remoting/webapp/crd/js/desktop_remoting.js b/remoting/webapp/crd/js/desktop_remoting.js
index 78ed95fd..29c32a0 100644
--- a/remoting/webapp/crd/js/desktop_remoting.js
+++ b/remoting/webapp/crd/js/desktop_remoting.js
@@ -14,27 +14,17 @@
 var remoting = remoting || {};
 
 /**
- * @param {Array<string>} appCapabilities Array of application capabilities.
  * @constructor
  * @implements {remoting.ApplicationInterface}
  * @extends {remoting.Application}
  */
-remoting.DesktopRemoting = function(appCapabilities) {
-  base.inherits(this, remoting.Application, appCapabilities);
-
-  /** @private {remoting.DesktopConnectedView} */
-  this.connectedView_ = null;
+remoting.DesktopRemoting = function() {
+  base.inherits(this, remoting.Application);
 
   /** @private {remoting.Activity} */
   this.activity_ = null;
 };
 
-/** @private */
-remoting.DesktopRemoting.prototype.reset_ = function() {
-  base.dispose(this.connectedView_);
-  this.connectedView_ = null;
-};
-
 /**
  * @return {string} Application product name to be used in UI.
  * @override {remoting.ApplicationInterface}
@@ -147,71 +137,6 @@
 };
 
 /**
- * @param {remoting.ConnectionInfo} connectionInfo
- * @override {remoting.ApplicationInterface}
- */
-remoting.DesktopRemoting.prototype.onConnected_ = function(connectionInfo) {
-  this.initSession_(connectionInfo);
-
-  remoting.setMode(remoting.AppMode.IN_SESSION);
-  if (!base.isAppsV2()) {
-    remoting.toolbar.center();
-    remoting.toolbar.preview();
-  }
-
-  this.connectedView_ = new remoting.DesktopConnectedView(
-      document.getElementById('client-container'), connectionInfo);
-
-  // By default, under ChromeOS, remap the right Control key to the right
-  // Win / Cmd key.
-  if (remoting.platformIsChromeOS()) {
-    connectionInfo.plugin().setRemapKeys('0x0700e4>0x0700e7');
-  }
-
-  if (connectionInfo.session().hasCapability(
-          remoting.ClientSession.Capability.VIDEO_RECORDER)) {
-    var recorder = new remoting.VideoFrameRecorder();
-    connectionInfo.plugin().extensions().register(recorder);
-    this.connectedView_.setVideoFrameRecorder(recorder);
-  }
-
-  this.activity_.onConnected(connectionInfo);
-};
-
-/**
- * @override {remoting.ApplicationInterface}
- */
-remoting.DesktopRemoting.prototype.onDisconnected_ = function() {
-  this.activity_.onDisconnected();
-  this.reset_();
-};
-
-/**
- * @param {!remoting.Error} error
- * @override {remoting.ApplicationInterface}
- */
-remoting.DesktopRemoting.prototype.onConnectionFailed_ = function(error) {
-  this.activity_.onConnectionFailed(error);
-};
-
-/**
- * @param {!remoting.Error} error The error to be localized and displayed.
- * @override {remoting.ApplicationInterface}
- */
-remoting.DesktopRemoting.prototype.onError_ = function(error) {
-  console.error('Connection failed: ' + error.toString());
-
-  if (error.hasTag(remoting.Error.Tag.AUTHENTICATION_FAILED)) {
-    remoting.setMode(remoting.AppMode.HOME);
-    remoting.handleAuthFailureAndRelaunch();
-    return;
-  }
-
-  this.activity_.onError(error);
-  this.reset_();
-};
-
-/**
  * Determine whether or not the app is running in a window.
  * @param {function(boolean):void} callback Callback to receive whether or not
  *     the current tab is running in windowed mode.
@@ -260,7 +185,8 @@
 
 /** @returns {remoting.DesktopConnectedView} */
 remoting.DesktopRemoting.prototype.getConnectedViewForTesting = function() {
-  return this.connectedView_;
+  var activity = /** @type {remoting.Me2MeActivity} */ (this.activity_);
+  return activity.getDesktopActivity().getConnectedView();
 };
 
 /**
@@ -272,14 +198,8 @@
  */
 remoting.DesktopRemoting.prototype.connectMe2Me_ = function(hostId) {
   var host = remoting.hostList.getHostForId(hostId);
-  // The Me2MeActivity triggers a reconnect underneath the hood on connection
-  // failure, but it won't notify the DesktopRemoting upon successful
-  // re-connection.  Therefore, we can't dispose the activity on connection
-  // failure.  Instead, the activity is only disposed when a new one is
-  // created.  This would be fixed once |sessionConnector| is moved out of the
-  // application.
   base.dispose(this.activity_);
-  this.activity_ = new remoting.Me2MeActivity(this.sessionConnector_, host);
+  this.activity_ = new remoting.Me2MeActivity(host);
   this.activity_.start();
 };
 
@@ -290,6 +210,6 @@
  */
 remoting.DesktopRemoting.prototype.connectIt2Me_ = function() {
   base.dispose(this.activity_);
-  this.activity_ = new remoting.It2MeActivity(this.sessionConnector_);
+  this.activity_ = new remoting.It2MeActivity();
   this.activity_.start();
 };
diff --git a/remoting/webapp/crd/js/desktop_remoting_activity.js b/remoting/webapp/crd/js/desktop_remoting_activity.js
new file mode 100644
index 0000000..768eccc
--- /dev/null
+++ b/remoting/webapp/crd/js/desktop_remoting_activity.js
@@ -0,0 +1,101 @@
+// 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.
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * This class provides common functionality to Me2MeActivity and It2MeActivity,
+ * e.g. constructing the relevant UX, and delegate the custom handling of the
+ * session state changes to the |parentActivity|.
+ *
+ * @param {remoting.Activity} parentActivity
+ * @implements {remoting.ClientSession.EventHandler}
+ * @implements {base.Disposable}
+ * @constructor
+ */
+remoting.DesktopRemotingActivity = function(parentActivity) {
+  /** @private */
+  this.parentActivity_ = parentActivity;
+  /** @private {remoting.DesktopConnectedView} */
+  this.connectedView_ = null;
+};
+
+/**
+ * @param {remoting.ConnectionInfo} connectionInfo
+ */
+remoting.DesktopRemotingActivity.prototype.onConnected =
+    function(connectionInfo) {
+  remoting.setMode(remoting.AppMode.IN_SESSION);
+  if (!base.isAppsV2()) {
+    remoting.toolbar.center();
+    remoting.toolbar.preview();
+  }
+
+  this.connectedView_ = new remoting.DesktopConnectedView(
+      document.getElementById('client-container'), connectionInfo);
+
+  // By default, under ChromeOS, remap the right Control key to the right
+  // Win / Cmd key.
+  if (remoting.platformIsChromeOS()) {
+    connectionInfo.plugin().setRemapKeys('0x0700e4>0x0700e7');
+  }
+
+  if (connectionInfo.session().hasCapability(
+          remoting.ClientSession.Capability.VIDEO_RECORDER)) {
+    var recorder = new remoting.VideoFrameRecorder();
+    connectionInfo.plugin().extensions().register(recorder);
+    this.connectedView_.setVideoFrameRecorder(recorder);
+  }
+
+  this.parentActivity_.onConnected(connectionInfo);
+};
+
+remoting.DesktopRemotingActivity.prototype.onDisconnected = function() {
+  this.parentActivity_.onDisconnected();
+  base.dispose(this.connectedView_);
+  this.connectedView_ = null;
+};
+
+/**
+ * @param {!remoting.Error} error
+ */
+remoting.DesktopRemotingActivity.prototype.onConnectionFailed =
+    function(error) {
+  this.parentActivity_.onConnectionFailed(error);
+};
+
+/**
+ * @param {!remoting.Error} error The error to be localized and displayed.
+ */
+remoting.DesktopRemotingActivity.prototype.onError = function(error) {
+  console.error('Connection failed: ' + error.toString());
+
+  if (error.hasTag(remoting.Error.Tag.AUTHENTICATION_FAILED)) {
+    remoting.setMode(remoting.AppMode.HOME);
+    remoting.handleAuthFailureAndRelaunch();
+    return;
+  }
+
+  this.parentActivity_.onError(error);
+
+  base.dispose(this.connectedView_);
+  this.connectedView_ = null;
+};
+
+remoting.DesktopRemotingActivity.prototype.dispose = function() {
+  base.dispose(this.connectedView_);
+  this.connectedView_ = null;
+};
+
+/** @return {remoting.DesktopConnectedView} */
+remoting.DesktopRemotingActivity.prototype.getConnectedView = function() {
+  return this.connectedView_;
+};
+
+})();
diff --git a/remoting/webapp/crd/js/host_controller.js b/remoting/webapp/crd/js/host_controller.js
index 0a88296..cde869a 100644
--- a/remoting/webapp/crd/js/host_controller.js
+++ b/remoting/webapp/crd/js/host_controller.js
@@ -72,7 +72,7 @@
     }
   };
 
-  hostDaemonFacade.getDaemonVersion(printVersion, function() {
+  hostDaemonFacade.getDaemonVersion().then(printVersion, function() {
     console.log('Host version not available.');
   });
 
@@ -90,24 +90,54 @@
 };
 
 /**
- * @param {remoting.HostController.Feature} feature The feature to test for.
- * @param {function(boolean):void} callback
- * @return {void}
+ * Information relating to user consent to collect usage stats.  The
+ * fields are:
+ *
+ *   supported: True if crash dump reporting is supported by the host.
+ *
+ *   allowed: True if crash dump reporting is allowed.
+ *
+ *   setByPolicy: True if crash dump reporting is controlled by policy.
+ *
+ * @typedef {{
+ *   supported:boolean,
+ *   allowed:boolean,
+ *   setByPolicy:boolean
+ * }}
  */
-remoting.HostController.prototype.hasFeature = function(feature, callback) {
+remoting.UsageStatsConsent;
+
+/**
+ * @typedef {{
+ *   userEmail:string,
+ *   refreshToken:string
+ * }}
+ */
+remoting.XmppCredentials;
+
+/**
+ * @typedef {{
+ *   privateKey:string,
+ *   publicKey:string
+ * }}
+ */
+remoting.KeyPair;
+
+/**
+ * @param {remoting.HostController.Feature} feature The feature to test for.
+ * @return {!Promise<boolean>} A promise that always resolves.
+ */
+remoting.HostController.prototype.hasFeature = function(feature) {
   // TODO(rmsousa): This could synchronously return a boolean, provided it were
   // only called after native messaging is completely initialized.
-  this.hostDaemonFacade_.hasFeature(feature, callback);
+  return this.hostDaemonFacade_.hasFeature(feature);
 };
 
 /**
- * @param {function(boolean, boolean, boolean):void} onDone Callback to be
- *     called when done.
- * @param {function(!remoting.Error):void} onError Callback to be called on
- *     error.
+ * @return {!Promise<remoting.UsageStatsConsent>}
  */
-remoting.HostController.prototype.getConsent = function(onDone, onError) {
-  this.hostDaemonFacade_.getUsageStatsConsent(onDone, onError);
+remoting.HostController.prototype.getConsent = function() {
+  return this.hostDaemonFacade_.getUsageStatsConsent();
 };
 
 /**
@@ -115,159 +145,48 @@
  *
  * @param {string} hostPin Host PIN.
  * @param {boolean} consent The user's consent to crash dump reporting.
- * @param {function():void} onDone Callback to be called when done.
- * @param {function(!remoting.Error):void} onError Callback to be called on
- *     error.
- * @return {void} Nothing.
+ * @return {!Promise<void>} A promise resolved once the host is started.
  */
-remoting.HostController.prototype.start = function(hostPin, consent, onDone,
-                                                   onError) {
+remoting.HostController.prototype.start = function(hostPin, consent) {
   /** @type {remoting.HostController} */
   var that = this;
 
+  // Start a bunch of requests with no side-effects.
+  var hostNamePromise = this.hostDaemonFacade_.getHostName();
+  var hasOauthPromise =
+      this.hasFeature(remoting.HostController.Feature.OAUTH_CLIENT);
+  var keyPairPromise = this.hostDaemonFacade_.generateKeyPair();
+  var oauthTokenPromise = remoting.identity.getToken();
+  var hostClientIdPromise = hasOauthPromise.then(function(hasOauth) {
+    if (hasOauth) {
+      return that.hostDaemonFacade_.getHostClientId();
+    } else {
+      return null;
+    }
+  });
   var newHostId = base.generateUuid();
+  var pinHashPromise = this.hostDaemonFacade_.getPinHash(newHostId, hostPin);
 
-  /** @param {!remoting.Error} error */
-  function onStartError(error) {
-    // Unregister the host if we failed to start it.
-    remoting.hostList.unregisterHostById(newHostId);
-    onError(error);
-  }
+  // Try to register the host.
+  /** @type {!Promise<!remoting.Xhr.Response>} */
+  var registrationResultPromise = Promise.all([
+    hostClientIdPromise,
+    hostNamePromise,
+    oauthTokenPromise,
+    keyPairPromise
+  ]).then(function(/** Array */ a) {
+    var hostClientId = /** @type {string} */ (a[0]);
+    var hostName = /** @type {string} */ (a[1]);
+    var oauthToken = /** @type {string} */ (a[2]);
+    var keyPair = /** @type {remoting.KeyPair} */ (a[3]);
 
-  /**
-   * @param {string} hostName
-   * @param {string} publicKey
-   * @param {remoting.HostController.AsyncResult} result
-   */
-  function onStarted(hostName, publicKey, result) {
-    if (result == remoting.HostController.AsyncResult.OK) {
-      remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
-      onDone();
-    } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
-      onStartError(new remoting.Error(remoting.Error.Tag.CANCELLED));
-    } else {
-      onStartError(remoting.Error.unexpected());
-    }
-  }
-
-  /**
-   * @param {string} hostName
-   * @param {string} publicKey
-   * @param {string} privateKey
-   * @param {?string} xmppLogin
-   * @param {?string} refreshToken
-   * @param {?string} clientBaseJid
-   * @param {string} hostSecretHash
-   */
-  function startHostWithHash(hostName, publicKey, privateKey, xmppLogin,
-                             refreshToken, clientBaseJid, hostSecretHash) {
-    var hostConfig = {
-      xmpp_login: xmppLogin,
-      oauth_refresh_token: refreshToken,
-      host_id: newHostId,
-      host_name: hostName,
-      host_secret_hash: hostSecretHash,
-      private_key: privateKey
-    };
-    var hostOwner = clientBaseJid;
-    remoting.identity.getEmail().then(
-        function(/** string */ hostOwnerEmail) {
-          if (hostOwner != xmppLogin) {
-            hostConfig['host_owner'] = hostOwner;
-            if (hostOwnerEmail != hostOwner) {
-              hostConfig['host_owner_email'] = hostOwnerEmail;
-            }
-          }
-          that.hostDaemonFacade_.startDaemon(
-              hostConfig, consent, onStarted.bind(null, hostName, publicKey),
-              onStartError);
-        });
-  }
-
-  /**
-   * @param {string} hostName
-   * @param {string} publicKey
-   * @param {string} privateKey
-   * @param {string} email
-   * @param {string} refreshToken
-   * @param {string} clientBaseJid
-   */
-  function onClientBaseJid(
-      hostName, publicKey, privateKey, email, refreshToken, clientBaseJid) {
-    that.hostDaemonFacade_.getPinHash(
-        newHostId, hostPin,
-        startHostWithHash.bind(null, hostName, publicKey, privateKey,
-                               email, refreshToken, clientBaseJid),
-        onError);
-  }
-
-  /**
-   * @param {string} hostName
-   * @param {string} publicKey
-   * @param {string} privateKey
-   * @param {string} email
-   * @param {string} refreshToken
-   */
-  function onServiceAccountCredentials(
-      hostName, publicKey, privateKey, email, refreshToken) {
-    that.getClientBaseJid_(
-        onClientBaseJid.bind(
-            null, hostName, publicKey, privateKey, email, refreshToken),
-        onStartError);
-  }
-
-  /**
-   * @param {string} hostName
-   * @param {string} publicKey
-   * @param {string} privateKey
-   * @param {!remoting.Xhr.Response} response
-   */
-  function onRegistered(
-      hostName, publicKey, privateKey, response) {
-    var success = (response.status == 200);
-
-    if (success) {
-      var result = base.jsonParseSafe(response.getText());
-      if ('data' in result && 'authorizationCode' in result['data']) {
-        that.hostDaemonFacade_.getCredentialsFromAuthCode(
-            result['data']['authorizationCode'],
-            onServiceAccountCredentials.bind(
-                null, hostName, publicKey, privateKey),
-            onError);
-      } else {
-        // No authorization code returned, use regular user credential flow.
-        remoting.identity.getEmail().then(
-            function(/** string */ email) {
-              that.hostDaemonFacade_.getPinHash(
-                  newHostId, hostPin, startHostWithHash.bind(
-                      null, hostName, publicKey, privateKey,
-                      email, remoting.oauth2.getRefreshToken(), email),
-                  onError);
-            });
-      }
-    } else {
-      console.log('Failed to register the host. Status: ' + response.status +
-                  ' response: ' + response.getText());
-      onError(new remoting.Error(remoting.Error.Tag.REGISTRATION_FAILED));
-    }
-  }
-
-  /**
-   * @param {string} hostName
-   * @param {string} privateKey
-   * @param {string} publicKey
-   * @param {?string} hostClientId
-   * @param {string} oauthToken
-   */
-  function doRegisterHost(
-      hostName, privateKey, publicKey, hostClientId, oauthToken) {
     var newHostDetails = { data: {
-       hostId: newHostId,
-       hostName: hostName,
-       publicKey: publicKey
+      hostId: newHostId,
+      hostName: hostName,
+      publicKey: keyPair.publicKey
     } };
 
-    new remoting.Xhr({
+    return new remoting.Xhr({
       method: 'POST',
       url: remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts',
       urlParams: {
@@ -275,63 +194,131 @@
       },
       jsonContent: newHostDetails,
       oauthToken: oauthToken
-    }).start().then(onRegistered.bind(null, hostName, publicKey, privateKey));
-  }
+    }).start();
+  });
 
-  /**
-   * @param {string} hostName
-   * @param {string} privateKey
-   * @param {string} publicKey
-   * @param {string} hostClientId
-   */
-  function onHostClientId(
-      hostName, privateKey, publicKey, hostClientId) {
-    remoting.identity.getToken().then(
-        doRegisterHost.bind(
-            null, hostName, privateKey, publicKey, hostClientId),
-        remoting.Error.handler(onError));
-  }
+  /** @type {boolean} */
+  var hostRegistered = false;
 
-  /**
-   * @param {string} hostName
-   * @param {string} privateKey
-   * @param {string} publicKey
-   * @param {boolean} hasFeature
-   */
-  function onHasFeatureOAuthClient(
-      hostName, privateKey, publicKey, hasFeature) {
-    if (hasFeature) {
-      that.hostDaemonFacade_.getHostClientId(
-          onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
+  // Extract an optional auth code from the host response.  The
+  // absence of an auth code is represented by an empty string.
+  /** @type {!Promise<string>} */
+  var authCodePromise = registrationResultPromise.then(function(response) {
+    if (response.status == 200) {
+      hostRegistered = true;
+      var result = base.jsonParseSafe(response.getText());
+      if (result['data']) {
+        return base.getStringAttr(result['data'], 'authorizationCode', '');
+      } else {
+        return '';
+      }
     } else {
-      remoting.identity.getToken().then(
-          doRegisterHost.bind(
-              null, hostName, privateKey, publicKey, null),
-          remoting.Error.handler(onError));
+      console.log(
+          'Failed to register the host. Status: ' + response.status +
+          ' response: ' + response.getText());
+      throw new remoting.Error(remoting.Error.Tag.REGISTRATION_FAILED);
     }
-  }
+  });
 
-  /**
-   * @param {string} hostName
-   * @param {string} privateKey
-   * @param {string} publicKey
-   */
-  function onKeyGenerated(hostName, privateKey, publicKey) {
-    that.hasFeature(
-        remoting.HostController.Feature.OAUTH_CLIENT,
-        onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
-  }
+  // Get XMPP creditials.
+  var xmppCredsPromise = authCodePromise.then(function(authCode) {
+    if (authCode) {
+      // Use auth code supplied by registry.
+      return that.hostDaemonFacade_.getCredentialsFromAuthCode(authCode);
+    } else {
+      // No authorization code returned, use regular Chrome
+      // identitial credential flow.
+      return remoting.identity.getEmail().then(function(/** string */ email) {
+        return {
+          userEmail: email,
+          refreshToken: remoting.oauth2.getRefreshToken()
+        };
+      });
+    }
+  });
 
-  /**
-   * @param {string} hostName
-   * @return {void} Nothing.
-   */
-  function startWithHostname(hostName) {
-    that.hostDaemonFacade_.generateKeyPair(onKeyGenerated.bind(null, hostName),
-                                         onError);
-  }
+  // Get as JID to use as the host owner.
+  var hostOwnerPromise = authCodePromise.then(function(authCode) {
+    if (authCode) {
+      return that.getClientBaseJid_();
+    } else {
+      return remoting.identity.getEmail();
+    }
+  });
 
-  this.hostDaemonFacade_.getHostName(startWithHostname, onError);
+  // Build an initial host configuration.
+  /** @type {!Promise<!Object>} */
+  var hostConfigNoOwnerPromise = Promise.all([
+    hostNamePromise,
+    pinHashPromise,
+    xmppCredsPromise,
+    keyPairPromise
+  ]).then(function(/** Array */ a) {
+    var hostName = /** @type {string} */ (a[0]);
+    var hostSecretHash = /** @type {string} */ (a[1]);
+    var xmppCreds = /** @type {remoting.XmppCredentials} */ (a[2]);
+    var keyPair = /** @type {remoting.KeyPair} */ (a[3]);
+    return {
+      xmpp_login: xmppCreds.userEmail,
+      oauth_refresh_token: xmppCreds.refreshToken,
+      host_id: newHostId,
+      host_name: hostName,
+      host_secret_hash: hostSecretHash,
+      private_key: keyPair.privateKey
+    };
+  });
+
+  // Add host_owner and host_owner_email fields to the host config if
+  // necessary.  This promise resolves to the same value as
+  // hostConfigNoOwnerPromise, with not until the extra fields are
+  // either added or determined to be redundant.
+  /** @type {!Promise<!Object>} */
+  var hostConfigWithOwnerPromise = Promise.all([
+    hostConfigNoOwnerPromise,
+    hostOwnerPromise,
+    remoting.identity.getEmail(),
+    xmppCredsPromise
+  ]).then(function(/** Array */ a) {
+    var hostConfig = /** @type {!Object} */ (a[0]);
+    var hostOwner = /** @type {string} */ (a[1]);
+    var hostOwnerEmail = /** @type {string} */ (a[2]);
+    var xmppCreds = /** @type {remoting.XmppCredentials} */ (a[3]);
+    if (hostOwner != xmppCreds.userEmail) {
+      hostConfig['host_owner'] = hostOwner;
+      if (hostOwnerEmail != hostOwner) {
+        hostConfig['host_owner_email'] = hostOwnerEmail;
+      }
+    }
+    return hostConfig;
+  });
+
+  // Start the daemon.
+  /** @type {!Promise<remoting.HostController.AsyncResult>} */
+  var startDaemonResultPromise =
+      hostConfigWithOwnerPromise.then(function(hostConfig) {
+        return that.hostDaemonFacade_.startDaemon(hostConfig, consent);
+      });
+
+  // Update the UI or report an error.
+  return startDaemonResultPromise.then(function(result) {
+    if (result == remoting.HostController.AsyncResult.OK) {
+      return hostNamePromise.then(function(hostName) {
+        return keyPairPromise.then(function(keyPair) {
+          remoting.hostList.onLocalHostStarted(
+              hostName, newHostId, keyPair.publicKey);
+        });
+      });
+    } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
+      throw new remoting.Error(remoting.Error.Tag.CANCELLED);
+    } else {
+      throw remoting.Error.unexpected();
+    }
+  }).catch(function(error) {
+    if (hostRegistered) {
+      remoting.hostList.unregisterHostById(newHostId);
+    }
+    throw error;
+  });
 };
 
 /**
@@ -365,7 +352,8 @@
     }
   }
 
-  this.hostDaemonFacade_.stopDaemon(onStopped, onError);
+  this.hostDaemonFacade_.stopDaemon().then(
+      onStopped, remoting.Error.handler(onError));
 };
 
 /**
@@ -407,8 +395,8 @@
     var newConfig = {
       host_secret_hash: pinHash
     };
-    that.hostDaemonFacade_.updateDaemonConfig(newConfig, onConfigUpdated,
-                                            onError);
+    that.hostDaemonFacade_.updateDaemonConfig(newConfig).then(
+        onConfigUpdated, remoting.Error.handler(onError));
   }
 
   /** @param {Object} config */
@@ -419,13 +407,14 @@
     }
     /** @type {string} */
     var hostId = config['host_id'];
-    that.hostDaemonFacade_.getPinHash(
-        hostId, newPin, updateDaemonConfigWithHash, onError);
+    that.hostDaemonFacade_.getPinHash(hostId, newPin).then(
+        updateDaemonConfigWithHash, remoting.Error.handler(onError));
   }
 
   // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
   // with an unprivileged version if that is necessary.
-  this.hostDaemonFacade_.getDaemonConfig(onConfig, onError);
+  this.hostDaemonFacade_.getDaemonConfig().then(
+      onConfig, remoting.Error.handler(onError));
 };
 
 /**
@@ -441,7 +430,8 @@
                remoting.HostController.State.NOT_INSTALLED :
                remoting.HostController.State.UNKNOWN);
   }
-  this.hostDaemonFacade_.getDaemonState(onDone, onError);
+  this.hostDaemonFacade_.getDaemonState().then(
+      onDone, remoting.Error.handler(onError));
 };
 
 /**
@@ -461,7 +451,7 @@
     onDone(hostId);
   };
 
-  this.hostDaemonFacade_.getDaemonConfig(onConfig, function(error) {
+  this.hostDaemonFacade_.getDaemonConfig().then(onConfig, function(error) {
     onDone(null);
   });
 };
@@ -475,7 +465,8 @@
  */
 remoting.HostController.prototype.getPairedClients = function(onDone,
                                                               onError) {
-  this.hostDaemonFacade_.getPairedClients(onDone, onError);
+  this.hostDaemonFacade_.getPairedClients().then(
+      onDone, remoting.Error.handler(onError));
 };
 
 /**
@@ -488,7 +479,8 @@
  */
 remoting.HostController.prototype.deletePairedClient = function(
     client, onDone, onError) {
-  this.hostDaemonFacade_.deletePairedClient(client, onDone, onError);
+  this.hostDaemonFacade_.deletePairedClient(client).then(
+      onDone, remoting.Error.handler(onError));
 };
 
 /**
@@ -500,7 +492,8 @@
  */
 remoting.HostController.prototype.clearPairedClients = function(
     onDone, onError) {
-  this.hostDaemonFacade_.clearPairedClients(onDone, onError);
+  this.hostDaemonFacade_.clearPairedClients().then(
+      onDone, remoting.Error.handler(onError));
 };
 
 /**
@@ -509,53 +502,45 @@
  * non-Gmail accounts, it may be different.
  *
  * @private
- * @param {function(string): void} onSuccess
- * @param {function(!remoting.Error): void} onError
+ * @return {!Promise<string>}
  */
-remoting.HostController.prototype.getClientBaseJid_ = function(
-    onSuccess, onError) {
+remoting.HostController.prototype.getClientBaseJid_ = function() {
   /** @type {remoting.SignalStrategy} */
   var signalStrategy = null;
 
-  /** @param {remoting.SignalStrategy.State} state */
-  var onState = function(state) {
-    switch (state) {
-      case remoting.SignalStrategy.State.CONNECTED:
+  var result = new Promise(function(resolve, reject) {
+    /** @param {remoting.SignalStrategy.State} state */
+    var onState = function(state) {
+      switch (state) {
+        case remoting.SignalStrategy.State.CONNECTED:
         var jid = signalStrategy.getJid().split('/')[0].toLowerCase();
         base.dispose(signalStrategy);
         signalStrategy = null;
-        onSuccess(jid);
+        resolve(jid);
         break;
 
-      case remoting.SignalStrategy.State.FAILED:
+        case remoting.SignalStrategy.State.FAILED:
         var error = signalStrategy.getError();
         base.dispose(signalStrategy);
         signalStrategy = null;
-        onError(error);
+        reject(error);
         break;
-    }
-  };
+      }
+    };
 
-  signalStrategy = remoting.SignalStrategy.create();
-  signalStrategy.setStateChangedCallback(onState);
+    signalStrategy = remoting.SignalStrategy.create();
+    signalStrategy.setStateChangedCallback(onState);
+  });
 
-  /** @param {string} token */
-  function connectSignalingWithToken(token) {
-    remoting.identity.getEmail().then(
-        connectSignalingWithTokenAndEmail.bind(null, token),
-        remoting.Error.handler(onError));
-  }
+  var tokenPromise = remoting.identity.getToken();
+  var emailPromise = remoting.identity.getEmail();
+  tokenPromise.then(function(/** string */ token) {
+    emailPromise.then(function(/** string */ email) {
+      signalStrategy.connect(remoting.settings.XMPP_SERVER, email, token);
+    });
+  });
 
-  /**
-   * @param {string} token
-   * @param {string} email
-   */
-  function connectSignalingWithTokenAndEmail(token, email) {
-    signalStrategy.connect(remoting.settings.XMPP_SERVER, email, token);
-  }
-
-  remoting.identity.getToken().then(
-      connectSignalingWithToken, remoting.Error.handler(onError));
+  return result;
 };
 
 /** @type {remoting.HostController} */
diff --git a/remoting/webapp/crd/js/host_controller_unittest.js b/remoting/webapp/crd/js/host_controller_unittest.js
index baf5390..f65dc97 100644
--- a/remoting/webapp/crd/js/host_controller_unittest.js
+++ b/remoting/webapp/crd/js/host_controller_unittest.js
@@ -51,7 +51,6 @@
 var FAKE_HOST_CLIENT_ID = '<FAKE_HOST_CLIENT_ID>';
 var FAKE_CLIENT_JID = '<FAKE_CLIENT_JID>';
 var FAKE_CLIENT_BASE_JID = '<FAKE_CLIENT_BASE_JID>';
-var FAKE_XMPP_LOGIN = '<FAKE_XMPP_LOGIN>';
 var FAKE_IDENTITY_TOKEN = '<FAKE_IDENTITY_TOKEN>';
 
 /** @type {sinon.Spy|Function} */
@@ -225,43 +224,18 @@
   });
 }
 
-// Check that hasFeature returns false for missing features.
-QUnit.test('hasFeature returns false', function(assert) {
-  return new Promise(function(resolve, reject) {
-    controller.hasFeature(
-        remoting.HostController.Feature.PAIRING_REGISTRY,
-        resolve);
-  }).then(function(/** boolean */ result) {
-    assert.equal(result, false);
-  });
-});
-
-// Check that hasFeature returns true for supported features.
-QUnit.test('hasFeature returns true', function(assert) {
-  return new Promise(function(resolve, reject) {
-    controller.hasFeature(
-        remoting.HostController.Feature.OAUTH_CLIENT,
-        resolve);
-  }).then(function(/** boolean */ result) {
-    assert.equal(result, true);
-  });
-});
-
 // Check what happens when the HostDaemonFacade's getHostName method
 // fails.
 QUnit.test('start with getHostName failure', function(assert) {
   mockHostDaemonFacade.hostName = null;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'getHostName');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'getHostName');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 0);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -270,17 +244,14 @@
 QUnit.test('start with generateKeyPair failure', function(assert) {
   mockHostDaemonFacade.publicKey = null;
   mockHostDaemonFacade.privateKey = null;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'generateKeyPair');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'generateKeyPair');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 0);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -288,17 +259,14 @@
 // method fails.
 QUnit.test('start with getHostClientId failure', function(assert) {
   mockHostDaemonFacade.hostClientId = null;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'getHostClientId');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'getHostClientId');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 0);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -307,16 +275,13 @@
 QUnit.test('start with host registration failure', function(assert) {
   remoting.MockXhr.setEmptyResponseFor(
       'POST', 'DIRECTORY_API_BASE_URL/@me/hosts', 500);
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getTag(), remoting.Error.Tag.REGISTRATION_FAILED);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getTag(), remoting.Error.Tag.REGISTRATION_FAILED);
+    assert.equal(unregisterHostByIdSpy.callCount, 0);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -326,18 +291,14 @@
   mockHostDaemonFacade.useEmail = null;
   mockHostDaemonFacade.refreshToken = null;
   queueRegistryResponse(assert, true);
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'getCredentialsFromAuthCode');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'getCredentialsFromAuthCode');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -347,17 +308,13 @@
 QUnit.test('start with getRefreshToken+getPinHash failure', function(assert) {
   mockHostDaemonFacade.pinHashFunc = null;
   queueRegistryResponse(assert, false);
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'getPinHash');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 0);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      assert.equal(startDaemonSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'getPinHash');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
+    assert.equal(startDaemonSpy.callCount, 0);
   });
 });
 
@@ -365,15 +322,12 @@
 QUnit.test('start with signalStrategy failure', function(assert) {
   queueRegistryResponse(assert, true);
   stubSignalStrategyConnect(false);
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'setStateForTesting');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 1);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'setStateForTesting');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 1);
   });
 });
 
@@ -384,17 +338,14 @@
   queueRegistryResponse(assert, true);
   stubSignalStrategyConnect(true);
   mockHostDaemonFacade.startDaemonResult = null;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getDetail(), 'startDaemon');
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 1);
-      assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getDetail(), 'startDaemon');
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 1);
+    assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
   });
 });
 
@@ -405,16 +356,13 @@
   stubSignalStrategyConnect(true);
   mockHostDaemonFacade.startDaemonResult =
       remoting.HostController.AsyncResult.CANCELLED;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
-      assert.equal(unregisterHostByIdSpy.callCount, 1);
-      assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getTag(), remoting.Error.Tag.CANCELLED);
+    assert.equal(unregisterHostByIdSpy.callCount, 1);
+    assert.equal(unregisterHostByIdSpy.args[0][0], FAKE_HOST_ID);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
   });
 });
 
@@ -425,53 +373,47 @@
   stubSignalStrategyConnect(true);
   mockHostDaemonFacade.startDaemonResult =
       remoting.HostController.AsyncResult.FAILED;
-  return new Promise(function(resolve, reject) {
-    controller.start(FAKE_HOST_PIN, true, function() {
-      reject('test failed');
-    }, function(/** remoting.Error */ e) {
-      assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
-      assert.equal(unregisterHostByIdSpy.callCount, 1);
-      assert.equal(onLocalHostStartedSpy.callCount, 0);
-      resolve(null);
-    });
+  return controller.start(FAKE_HOST_PIN, true).then(function() {
+    throw 'test failed';
+  }, function(/** remoting.Error */ e) {
+    assert.equal(e.getTag(), remoting.Error.Tag.UNEXPECTED);
+    assert.equal(unregisterHostByIdSpy.callCount, 1);
+    assert.equal(onLocalHostStartedSpy.callCount, 0);
   });
 });
 
 // Check what happens when the entire host registration process
 // succeeds.
 [false, true].forEach(function(/** boolean */ consent) {
-  QUnit.test('start succeeds(1) with consent=' + consent, function(assert) {
+  QUnit.test('start with auth code, consent=' + consent, function(assert) {
     /** @const */
     var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_HOST_PIN);
     queueRegistryResponse(assert, true);
     stubSignalStrategyConnect(true);
-    return new Promise(function(resolve, reject) {
-      controller.start(FAKE_HOST_PIN, consent, function() {
-        assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
-        assert.deepEqual(
-            getCredentialsFromAuthCodeSpy.args[0][0],
-            FAKE_AUTH_CODE);
-        assert.equal(getPinHashSpy.callCount, 1);
-        assert.deepEqual(
-            getPinHashSpy.args[0].slice(0, 2),
-            [FAKE_HOST_ID, FAKE_HOST_PIN]);
-        assert.equal(unregisterHostByIdSpy.callCount, 0);
-        assert.equal(onLocalHostStartedSpy.callCount, 1);
-        assert.equal(startDaemonSpy.callCount, 1);
-        assert.deepEqual(
-            startDaemonSpy.args[0].slice(0, 2),
-            [{
-              xmpp_login: FAKE_XMPP_LOGIN,
-              oauth_refresh_token: FAKE_REFRESH_TOKEN,
-              host_owner: FAKE_CLIENT_JID.toLowerCase(),
-              host_owner_email: FAKE_USER_EMAIL,
-              host_id: FAKE_HOST_ID,
-              host_name: FAKE_HOST_NAME,
-              host_secret_hash: fakePinHash,
-              private_key: FAKE_PRIVATE_KEY
-            }, consent]);
-        resolve(null);
-      }, reject);
+    return controller.start(FAKE_HOST_PIN, consent).then(function() {
+      assert.equal(getCredentialsFromAuthCodeSpy.callCount, 1);
+      assert.deepEqual(
+          getCredentialsFromAuthCodeSpy.args[0][0],
+          FAKE_AUTH_CODE);
+      assert.equal(getPinHashSpy.callCount, 1);
+      assert.deepEqual(
+          getPinHashSpy.args[0].slice(0, 2),
+          [FAKE_HOST_ID, FAKE_HOST_PIN]);
+      assert.equal(unregisterHostByIdSpy.callCount, 0);
+      assert.equal(onLocalHostStartedSpy.callCount, 1);
+      assert.equal(startDaemonSpy.callCount, 1);
+      assert.deepEqual(
+          startDaemonSpy.args[0].slice(0, 2),
+          [{
+            xmpp_login: FAKE_XMPP_LOGIN,
+            oauth_refresh_token: FAKE_REFRESH_TOKEN,
+            host_owner: FAKE_CLIENT_JID.toLowerCase(),
+            host_owner_email: FAKE_USER_EMAIL,
+            host_id: FAKE_HOST_ID,
+            host_name: FAKE_HOST_NAME,
+            host_secret_hash: fakePinHash,
+            private_key: FAKE_PRIVATE_KEY
+          }, consent]);
     });
   });
 });
@@ -479,32 +421,30 @@
 // Check alternative host registration without a registry-supplied
 // auth code.
 [false, true].forEach(function(/** boolean */ consent) {
-  QUnit.test('start succeeds(2) with consent=' + consent, function(assert) {
+  QUnit.test('start without auth code, consent=' + consent, function(assert) {
     /** @const */
     var fakePinHash = fakePinHashFunc(FAKE_HOST_ID, FAKE_HOST_PIN);
     queueRegistryResponse(assert, false);
     stubSignalStrategyConnect(true);
-    return new Promise(function(resolve, reject) {
-      controller.start(FAKE_HOST_PIN, consent, function() {
-        assert.equal(getPinHashSpy.callCount, 1);
-        assert.deepEqual(
-            getPinHashSpy.args[0].slice(0, 2),
-            [FAKE_HOST_ID, FAKE_HOST_PIN]);
-        assert.equal(unregisterHostByIdSpy.callCount, 0);
-        assert.equal(onLocalHostStartedSpy.callCount, 1);
-        assert.equal(startDaemonSpy.callCount, 1);
-        assert.deepEqual(
-            startDaemonSpy.args[0].slice(0, 2),
-            [{
-              xmpp_login: FAKE_USER_EMAIL,
-              oauth_refresh_token: FAKE_REFRESH_TOKEN,
-              host_id: FAKE_HOST_ID,
-              host_name: FAKE_HOST_NAME,
-              host_secret_hash: fakePinHash,
-              private_key: FAKE_PRIVATE_KEY
-            }, consent]);
-        resolve(null);
-      }, reject);
+    return controller.start(FAKE_HOST_PIN, consent).then(function() {
+      assert.equal(getCredentialsFromAuthCodeSpy.callCount, 0);
+      assert.equal(getPinHashSpy.callCount, 1);
+      assert.deepEqual(
+          getPinHashSpy.args[0].slice(0, 2),
+          [FAKE_HOST_ID, FAKE_HOST_PIN]);
+      assert.equal(unregisterHostByIdSpy.callCount, 0);
+      assert.equal(onLocalHostStartedSpy.callCount, 1);
+      assert.equal(startDaemonSpy.callCount, 1);
+      assert.deepEqual(
+          startDaemonSpy.args[0].slice(0, 2),
+          [{
+            xmpp_login: FAKE_USER_EMAIL,
+            oauth_refresh_token: FAKE_REFRESH_TOKEN,
+            host_id: FAKE_HOST_ID,
+            host_name: FAKE_HOST_NAME,
+            host_secret_hash: fakePinHash,
+            private_key: FAKE_PRIVATE_KEY
+          }, consent]);
     });
   });
 });
@@ -674,8 +614,8 @@
 
 // Check what happens when getLocalHostState reports no plugin.
 QUnit.test('getLocalHostState with no plugin', function(assert) {
-  sinon.stub(mockHostDaemonFacade, 'getDaemonState').
-      callsArgWith(1, new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
+  sinon.stub(mockHostDaemonFacade, 'getDaemonState').returns(
+      Promise.reject(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)));
   return new Promise(function(resolve, reject) {
     controller.getLocalHostState(function(
         /** remoting.HostController.State */ state) {
@@ -729,8 +669,8 @@
   });
 });
 
-// Tests omitted for getPairedClients, deletePairedClient, and
-// clearPairedClients because they simply call through to
+// Tests omitted for hasFeature, getPairedClients, deletePairedClient,
+// and clearPairedClients because they simply call through to
 // HostDaemonFacade.
 
 })();
diff --git a/remoting/webapp/crd/js/host_daemon_facade.js b/remoting/webapp/crd/js/host_daemon_facade.js
index 6b9d9c1..0596b57 100644
--- a/remoting/webapp/crd/js/host_daemon_facade.js
+++ b/remoting/webapp/crd/js/host_daemon_facade.js
@@ -84,54 +84,46 @@
  * @private
  */
 remoting.HostDaemonFacade.prototype.connectNative_ = function() {
-  /**
-   * @this {remoting.HostDaemonFacade}
-   * @param {function(?):void} resolve
-   * @param {function(*):void} reject
-   */
-  var connect = function(resolve, reject) {
-    try {
-      this.port_ = chrome.runtime.connectNative(
-          'com.google.chrome.remote_desktop');
-      this.port_.onMessage.addListener(this.onIncomingMessageCallback_);
-      this.port_.onDisconnect.addListener(this.onDisconnectCallback_);
-      this.postMessageInternal_({type: 'hello'}, resolve, reject);
-    } catch (/** @type {*} */ err) {
-      console.log('Native Messaging initialization failed: ', err);
-      reject(false);
-    }
-  };
-
-  return new Promise(connect.bind(this));
+  try {
+    this.port_ = chrome.runtime.connectNative(
+        'com.google.chrome.remote_desktop');
+    this.port_.onMessage.addListener(this.onIncomingMessageCallback_);
+    this.port_.onDisconnect.addListener(this.onDisconnectCallback_);
+    return this.postMessageInternal_({type: 'hello'});
+  } catch (/** @type {*} */ err) {
+    console.log('Native Messaging initialization failed: ', err);
+    throw remoting.Error.unexpected();
+  }
 };
 
 /**
  * Type used for entries of |pendingReplies_| list.
  *
  * @param {string} type Type of the originating request.
- * @param {function(...):void} onDone Response callback. Parameters depend on
- *     the request type.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
+ * @param {!base.Deferred} deferred Used to communicate returns back
+ *     to the caller.
  * @constructor
  */
-remoting.HostDaemonFacade.PendingReply = function(type, onDone, onError) {
+remoting.HostDaemonFacade.PendingReply = function(type, deferred) {
+  /** @const */
   this.type = type;
-  this.onDone = onDone;
-  this.onError = onError;
+
+  /** @const */
+  this.deferred = deferred;
 };
 
 /**
  * @param {remoting.HostController.Feature} feature The feature to test for.
- * @param {function(boolean):void} onDone Callback to return result.
- * @return {boolean} True if the implementation supports the named feature.
+ * @return {!Promise<boolean>} True if the implementation supports the
+ *     named feature.
  */
-remoting.HostDaemonFacade.prototype.hasFeature = function(feature, onDone) {
+remoting.HostDaemonFacade.prototype.hasFeature = function(feature) {
   /** @type {remoting.HostDaemonFacade} */
   var that = this;
-  this.initialize_().then(function() {
-    onDone(that.supportedFeatures_.indexOf(feature) >= 0);
-  }, function (){
-    onDone(false);
+  return this.initialize_().then(function() {
+    return that.supportedFeatures_.indexOf(feature) >= 0;
+  }, function () {
+    return false;
   });
 };
 
@@ -139,42 +131,38 @@
  * Initializes that the Daemon if necessary and posts the supplied message.
  *
  * @param {{type: string}} message The message to post.
- * @param {function(...):void} onDone The callback, if any, to be triggered
- *     on response.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
+ * @return {!Promise}
  * @private
  */
 remoting.HostDaemonFacade.prototype.postMessage_ =
-    function(message, onDone, onError) {
+    function(message) {
   /** @type {remoting.HostDaemonFacade} */
   var that = this;
-  this.initialize_().then(function() {
-    that.postMessageInternal_(message, onDone, onError);
+  return this.initialize_().then(function() {
+    return that.postMessageInternal_(message);
   }, function() {
-    onError(that.error_);
+    throw that.error_;
   });
 };
 
 /**
- * Attaches a new ID to the supplied message, and posts it to the Native
- * Messaging port, adding |onDone| to the list of pending replies.
- * |message| should have its 'type' field set, and any other fields set
- * depending on the message type.
+ * Attaches a new ID to the supplied message, and posts it to the
+ * Native Messaging port, adding a Deferred object to the list of
+ * pending replies.  |message| should have its 'type' field set, and
+ * any other fields set depending on the message type.
  *
  * @param {{type: string}} message The message to post.
- * @param {function(...):void} onDone The callback, if any, to be triggered
- *     on response.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise}
  * @private
  */
-remoting.HostDaemonFacade.prototype.postMessageInternal_ =
-    function(message, onDone, onError) {
+remoting.HostDaemonFacade.prototype.postMessageInternal_ = function(message) {
   var id = this.nextId_++;
   message['id'] = id;
+  var deferred = new base.Deferred();
   this.pendingReplies_[id] = new remoting.HostDaemonFacade.PendingReply(
-    message.type + 'Response', onDone, onError);
+    message.type + 'Response', deferred);
   this.port_.postMessage(message);
+  return deferred.promise();
 };
 
 /**
@@ -204,24 +192,34 @@
       throw 'Expected reply type: ' + reply.type + ', got: ' + type;
     }
 
-    this.handleIncomingMessage_(message, reply.onDone);
+    reply.deferred.resolve(this.handleIncomingMessage_(message));
   } catch (/** @type {*} */ e) {
     console.error('Error while processing native message', e);
-    reply.onError(remoting.Error.unexpected());
+    reply.deferred.reject(remoting.Error.unexpected());
   }
-}
+};
 
 /**
  * Handler for incoming Native Messages.
  *
+ * TODO(jrw) Consider refactoring so each method handles its own
+ * response, e.g.:
+ *
+ * remoting.HostDaemonFacade.prototype.generateKeyPair = function() {
+ *   this.postMessage_({type: 'generateKeyPair'}).then(function(message) {
+ *     return {
+ *       privateKey: base.getStringAttr(message, 'privateKey'),
+ *       publicKey: base.getStringAttr(message, 'publicKey')
+ *     }
+ *   });
+ * };
+ *
  * @param {Object} message The received message.
- * @param {function(...):void} onDone Function to call when we're done
- *     processing the message.
- * @return {void} Nothing.
+ * @return {*}
  * @private
  */
 remoting.HostDaemonFacade.prototype.handleIncomingMessage_ =
-    function(message, onDone) {
+    function(message) {
   var type = base.getStringAttr(message, 'type');
 
   switch (type) {
@@ -231,81 +229,70 @@
       // Those versions default to the empty list of supported features.
       this.supportedFeatures_ =
           base.getArrayAttr(message, 'supportedFeatures', []);
-      onDone();
-      break;
+      return null;
 
     case 'getHostNameResponse':
-      onDone(base.getStringAttr(message, 'hostname'));
-      break;
+      return base.getStringAttr(message, 'hostname');
 
     case 'getPinHashResponse':
-      onDone(base.getStringAttr(message, 'hash'));
-      break;
+      return base.getStringAttr(message, 'hash');
 
     case 'generateKeyPairResponse':
-      var privateKey = base.getStringAttr(message, 'privateKey');
-      var publicKey = base.getStringAttr(message, 'publicKey');
-      onDone(privateKey, publicKey);
-      break;
+      return {
+        privateKey: base.getStringAttr(message, 'privateKey'),
+        publicKey: base.getStringAttr(message, 'publicKey')
+      };
 
     case 'updateDaemonConfigResponse':
-      var result = remoting.HostController.AsyncResult.fromString(
+      return remoting.HostController.AsyncResult.fromString(
           base.getStringAttr(message, 'result'));
-      onDone(result);
-      break;
 
     case 'getDaemonConfigResponse':
-      onDone(base.getObjectAttr(message, 'config'));
-      break;
+      return base.getObjectAttr(message, 'config');
 
     case 'getUsageStatsConsentResponse':
-      var supported = base.getBooleanAttr(message, 'supported');
-      var allowed = base.getBooleanAttr(message, 'allowed');
-      var setByPolicy = base.getBooleanAttr(message, 'setByPolicy');
-      onDone(supported, allowed, setByPolicy);
-      break;
+      return {
+        supported: base.getBooleanAttr(message, 'supported'),
+        allowed: base.getBooleanAttr(message, 'allowed'),
+        setByPolicy: base.getBooleanAttr(message, 'setByPolicy')
+      };
 
     case 'startDaemonResponse':
     case 'stopDaemonResponse':
-      var result = remoting.HostController.AsyncResult.fromString(
+      return remoting.HostController.AsyncResult.fromString(
           base.getStringAttr(message, 'result'));
-      onDone(result);
-      break;
 
     case 'getDaemonStateResponse':
-      var state = remoting.HostController.State.fromString(
+      return remoting.HostController.State.fromString(
         base.getStringAttr(message, 'state'));
-      onDone(state);
-      break;
 
     case 'getPairedClientsResponse':
       var pairedClients = remoting.PairedClient.convertToPairedClientArray(
           message['pairedClients']);
       if (pairedClients != null) {
-        onDone(pairedClients);
+        return pairedClients;
       } else {
         throw 'No paired clients!';
       }
-      break;
 
     case 'clearPairedClientsResponse':
     case 'deletePairedClientResponse':
-      onDone(base.getBooleanAttr(message, 'result'));
-      break;
+      return base.getBooleanAttr(message, 'result');
 
     case 'getHostClientIdResponse':
-      onDone(base.getStringAttr(message, 'clientId'));
-      break;
+      return base.getStringAttr(message, 'clientId');
 
     case 'getCredentialsFromAuthCodeResponse':
       var userEmail = base.getStringAttr(message, 'userEmail');
       var refreshToken = base.getStringAttr(message, 'refreshToken');
       if (userEmail && refreshToken) {
-        onDone(userEmail, refreshToken);
+        return {
+          userEmail: userEmail,
+          refreshToken: refreshToken
+        };
       } else {
         throw 'Missing userEmail or refreshToken';
       }
-      break;
 
     default:
       throw 'Unexpected native message: ' + message;
@@ -334,20 +321,17 @@
   this.pendingReplies_ = {};
   for (var id in pendingReplies) {
     var num_id = parseInt(id, 10);
-    pendingReplies[num_id].onError(this.error_);
+    pendingReplies[num_id].deferred.reject(this.error_);
   }
 }
 
 /**
  * Gets local hostname.
  *
- * @param {function(string):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<string>}
  */
-remoting.HostDaemonFacade.prototype.getHostName =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getHostName'}, onDone, onError);
+remoting.HostDaemonFacade.prototype.getHostName = function() {
+  return this.postMessage_({type: 'getHostName'});
 };
 
 /**
@@ -356,17 +340,14 @@
  *
  * @param {string} hostId The host ID.
  * @param {string} pin The PIN.
- * @param {function(string):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<string>}
  */
-remoting.HostDaemonFacade.prototype.getPinHash =
-    function(hostId, pin, onDone, onError) {
-  this.postMessage_({
+remoting.HostDaemonFacade.prototype.getPinHash = function(hostId, pin) {
+  return this.postMessage_({
       type: 'getPinHash',
       hostId: hostId,
       pin: pin
-  }, onDone, onError);
+  });
 };
 
 /**
@@ -374,13 +355,10 @@
  * when the key is generated. The key is returned in format understood by the
  * host (PublicKeyInfo structure encoded with ASN.1 DER, and then BASE64).
  *
- * @param {function(string, string):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.KeyPair>}
  */
-remoting.HostDaemonFacade.prototype.generateKeyPair =
-    function(onDone, onError) {
-  this.postMessage_({type: 'generateKeyPair'}, onDone, onError);
+remoting.HostDaemonFacade.prototype.generateKeyPair = function() {
+  return this.postMessage_({type: 'generateKeyPair'});
 };
 
 /**
@@ -391,51 +369,40 @@
  * includes these parameters. Changes take effect before the callback
  * is called.
  *
- * TODO(jrw): Consider eliminating onError callback.
+ * TODO(jrw): Consider conversion exceptions to AsyncResult values.
  *
  * @param {Object} config The new config parameters.
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- *     Callback to be called when finished.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
-remoting.HostDaemonFacade.prototype.updateDaemonConfig =
-    function(config, onDone, onError) {
-  this.postMessage_({
+remoting.HostDaemonFacade.prototype.updateDaemonConfig = function(config) {
+  return this.postMessage_({
       type: 'updateDaemonConfig',
       config: config
-  }, onDone, onError);
+  });
 };
 
 /**
  * Loads daemon config. The config is passed as a JSON formatted string to the
  * callback.
- *
- * @param {function(Object):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<Object>}
  */
-remoting.HostDaemonFacade.prototype.getDaemonConfig =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getDaemonConfig'}, onDone, onError);
+remoting.HostDaemonFacade.prototype.getDaemonConfig = function() {
+  return this.postMessage_({type: 'getDaemonConfig'});
 };
 
 /**
- * Retrieves daemon version. The version is passed to onDone as a dotted decimal
+ * Retrieves daemon version. The version is returned as a dotted decimal
  * string of the form major.minor.build.patch.
  *
- * @param {function(string):void} onDone Callback to be called to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void}
+ * @return {!Promise<string>}
  */
-remoting.HostDaemonFacade.prototype.getDaemonVersion =
-    function(onDone, onError) {
+remoting.HostDaemonFacade.prototype.getDaemonVersion = function() {
   /** @type {remoting.HostDaemonFacade} */
   var that = this;
-  this.initialize_().then(function() {
-    onDone(that.version_);
+  return this.initialize_().then(function() {
+    return that.version_;
   }, function() {
-    onError(that.error_);
+    throw that.error_;
   });
 };
 
@@ -443,126 +410,99 @@
  * Get the user's consent to crash reporting. The consent flags are passed to
  * the callback as booleans: supported, allowed, set-by-policy.
  *
- * @param {function(boolean, boolean, boolean):void} onDone Callback to return
- *     result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.UsageStatsConsent>}
  */
-remoting.HostDaemonFacade.prototype.getUsageStatsConsent =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getUsageStatsConsent'}, onDone, onError);
+remoting.HostDaemonFacade.prototype.getUsageStatsConsent = function() {
+  return this.postMessage_({type: 'getUsageStatsConsent'});
 };
 
 /**
  * Starts the daemon process with the specified configuration.
  *
- * TODO(jrw): Consider eliminating onError callback.
+ * TODO(jrw): Consider conversion exceptions to AsyncResult values.
  *
  * @param {Object} config Host configuration.
  * @param {boolean} consent Consent to report crash dumps.
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- *     Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
-remoting.HostDaemonFacade.prototype.startDaemon =
-    function(config, consent, onDone, onError) {
-  this.postMessage_({
+remoting.HostDaemonFacade.prototype.startDaemon = function(config, consent) {
+  return this.postMessage_({
       type: 'startDaemon',
       config: config,
       consent: consent
-  }, onDone, onError);
+  });
 };
 
 /**
  * Stops the daemon process.
  *
- * TODO(jrw): Consider eliminating onError callback.
+ * TODO(jrw): Consider conversion exceptions to AsyncResult values.
  *
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- *     Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
 remoting.HostDaemonFacade.prototype.stopDaemon =
-    function(onDone, onError) {
-  this.postMessage_({type: 'stopDaemon'}, onDone, onError);
+    function() {
+  return this.postMessage_({type: 'stopDaemon'});
 };
 
 /**
  * Gets the installed/running state of the Host process.
  *
- * @param {function(remoting.HostController.State):void} onDone Callback to
-*      return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<remoting.HostController.State>}
  */
-remoting.HostDaemonFacade.prototype.getDaemonState =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getDaemonState'}, onDone, onError);
-}
+remoting.HostDaemonFacade.prototype.getDaemonState = function() {
+  return this.postMessage_({type: 'getDaemonState'});
+};
 
 /**
  * Retrieves the list of paired clients.
  *
- * @param {function(Array<remoting.PairedClient>):void} onDone Callback to
- *     return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
+ * @return {!Promise<Array<remoting.PairedClient>>}
  */
-remoting.HostDaemonFacade.prototype.getPairedClients =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getPairedClients'}, onDone, onError);
-}
+remoting.HostDaemonFacade.prototype.getPairedClients = function() {
+  return this.postMessage_({type: 'getPairedClients'});
+};
 
 /**
  * Clears all paired clients from the registry.
  *
- * @param {function(boolean):void} onDone Callback to be called when finished.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
+ * @return {!Promise<boolean>}
  */
-remoting.HostDaemonFacade.prototype.clearPairedClients =
-    function(onDone, onError) {
-  this.postMessage_({type: 'clearPairedClients'}, onDone, onError);
-}
+remoting.HostDaemonFacade.prototype.clearPairedClients = function() {
+  return this.postMessage_({type: 'clearPairedClients'});
+};
 
 /**
  * Deletes a paired client referenced by client id.
  *
  * @param {string} client Client to delete.
- * @param {function(boolean):void} onDone Callback to be called when finished.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
+ * @return {!Promise<boolean>}
  */
-remoting.HostDaemonFacade.prototype.deletePairedClient =
-    function(client, onDone, onError) {
-  this.postMessage_({
+remoting.HostDaemonFacade.prototype.deletePairedClient = function(client) {
+  return this.postMessage_({
     type: 'deletePairedClient',
     clientId: client
-  }, onDone, onError);
-}
+  });
+};
 
 /**
  * Gets the API keys to obtain/use service account credentials.
  *
- * @param {function(string):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<string>}
  */
-remoting.HostDaemonFacade.prototype.getHostClientId =
-    function(onDone, onError) {
-  this.postMessage_({type: 'getHostClientId'}, onDone, onError);
+remoting.HostDaemonFacade.prototype.getHostClientId = function() {
+  return this.postMessage_({type: 'getHostClientId'});
 };
 
 /**
  *
  * @param {string} authorizationCode OAuth authorization code.
- * @param {function(string, string):void} onDone Callback to return result.
- * @param {function(!remoting.Error):void} onError Callback to call on error.
- * @return {void} Nothing.
+ * @return {!Promise<{remoting.XmppCredentials}>}
  */
 remoting.HostDaemonFacade.prototype.getCredentialsFromAuthCode =
-    function(authorizationCode, onDone, onError) {
-  this.postMessage_({
+    function(authorizationCode) {
+  return this.postMessage_({
     type: 'getCredentialsFromAuthCode',
     authorizationCode: authorizationCode
-  }, onDone, onError);
+  });
 };
diff --git a/remoting/webapp/crd/js/host_daemon_facade_unittest.js b/remoting/webapp/crd/js/host_daemon_facade_unittest.js
index 0a3cf51..f7f6ad2 100644
--- a/remoting/webapp/crd/js/host_daemon_facade_unittest.js
+++ b/remoting/webapp/crd/js/host_daemon_facade_unittest.js
@@ -68,12 +68,9 @@
     id: 0,
     type: 'hello'
   });
-  var done = assert.async();
-  it.hasFeature(
-      remoting.HostController.Feature.PAIRING_REGISTRY,
-      function onDone(arg) {
+  return it.hasFeature(remoting.HostController.Feature.PAIRING_REGISTRY).
+      then(function(arg) {
         assert.equal(arg, true);
-        done();
       });
 });
 
@@ -89,12 +86,9 @@
     id: 0,
     type: 'hello'
   });
-  var done = assert.async();
-  it.hasFeature(
-      remoting.HostController.Feature.PAIRING_REGISTRY,
-      function onDone(arg) {
+  return it.hasFeature(remoting.HostController.Feature.PAIRING_REGISTRY).
+      then(function(arg) {
         assert.equal(arg, false);
-        done();
       });
 });
 
@@ -110,15 +104,9 @@
     id: 0,
     type: 'hello'
   });
-  var done = assert.async();
-  it.getDaemonVersion(
-      function onDone(arg) {
+  return it.getDaemonVersion().
+      then(function onDone(arg) {
         assert.equal(arg, '<daemonVersion>');
-        done();
-      },
-      function onError() {
-        assert.ok(false);
-        done();
       });
 });
 
@@ -139,36 +127,19 @@
       id: 0,
       type: 'hello'
     });
-    return new Promise(function(resolve, reject) {
-      it.getDaemonVersion(resolve, reject);
-    }).then(function() {
+    it.getDaemonVersion().then(function() {
       return callback(assert);
     });
   });
 }
 
-/**
- * A combinator that turns an ordinary 1-argument resolve function
- * into a function that accepts multiple arguments and bundles them
- * into an array.
- * @param {function(*):void} resolve
- * @return {function(...*):void}
- */
-function resolveMulti(resolve) {
-  return function() {
-    resolve(Array.prototype.slice.call(arguments));
-  };
-}
-
 postInitTest('getHostName', function(assert) {
   mockHostResponses.push({
     id: 1,
     type: 'getHostNameResponse',
     hostname: '<fakeHostName>'
   });
-  return new Promise(function (resolve, reject) {
-    it.getHostName(resolve, reject);
-  }).then(function(/** string */ hostName) {
+  return it.getHostName().then(function(hostName) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getHostName'
@@ -183,9 +154,7 @@
     type: 'getPinHashResponse',
     hash: '<fakePinHash>'
   });
-  return new Promise(function (resolve, reject) {
-    it.getPinHash('<hostId>', '<pin>', resolve, reject);
-  }).then(function(/** string */ hostName) {
+  return it.getPinHash('<hostId>', '<pin>').then(function(hostName) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getPinHash',
@@ -203,14 +172,15 @@
     privateKey: '<fakePrivateKey>',
     publicKey: '<fakePublicKey>'
   });
-  return new Promise(function (resolve, reject) {
-    it.generateKeyPair(resolveMulti(resolve), reject);
-  }).then(function(/** Array */ result) {
+  return it.generateKeyPair().then(function(pair) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'generateKeyPair'
     });
-    assert.deepEqual(result, ['<fakePrivateKey>', '<fakePublicKey>']);
+    assert.deepEqual(pair, {
+      privateKey: '<fakePrivateKey>',
+      publicKey: '<fakePublicKey>'
+    });
   });
 });
 
@@ -220,9 +190,9 @@
     type: 'updateDaemonConfigResponse',
     result: 'OK'
   });
-  return new Promise(function (resolve, reject) {
-    it.updateDaemonConfig({ fakeDaemonConfig: true }, resolve, reject);
-  }).then(function(/** * */ result) {
+  return it.updateDaemonConfig({
+    fakeDaemonConfig: true
+  }).then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'updateDaemonConfig',
@@ -238,9 +208,7 @@
     type: 'getDaemonConfigResponse',
     config: { fakeDaemonConfig: true }
   });
-  return new Promise(function (resolve, reject) {
-    it.getDaemonConfig(resolve, reject);
-  }).then(function(/** * */ result) {
+  return it.getDaemonConfig().then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getDaemonConfig'
@@ -261,14 +229,16 @@
       allowed: allowed,
       setByPolicy: setByPolicy
     });
-    return new Promise(function (resolve, reject) {
-      it.getUsageStatsConsent(resolveMulti(resolve), reject);
-    }).then(function(/** * */ result) {
+    return it.getUsageStatsConsent().then(function(result) {
       assert.deepEqual(postMessageStub.args[1][0], {
         id: 1,
         type: 'getUsageStatsConsent'
       });
-      assert.deepEqual(result, [supported, allowed, setByPolicy]);
+      assert.deepEqual(result, {
+        supported: supported,
+        allowed: allowed,
+        setByPolicy: setByPolicy
+      });
     });
   });
 });
@@ -280,9 +250,9 @@
       type: 'startDaemonResponse',
       result: 'FAILED'
     });
-    return new Promise(function (resolve, reject) {
-      it.startDaemon({ fakeConfig: true }, consent, resolve, reject);
-    }).then(function(/** * */ result) {
+    return it.startDaemon({
+      fakeConfig: true
+    }, consent).then(function(result) {
       assert.deepEqual(postMessageStub.args[1][0], {
         id: 1,
         type: 'startDaemon',
@@ -300,9 +270,7 @@
     type: 'stopDaemonResponse',
     result: 'CANCELLED'
   });
-  return new Promise(function (resolve, reject) {
-    it.stopDaemon(resolve, reject);
-  }).then(function(/** * */ result) {
+  return it.stopDaemon().then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'stopDaemon'
@@ -331,9 +299,7 @@
     type: 'getPairedClientsResponse',
     pairedClients: [client0, client1]
   });
-  return new Promise(function (resolve, reject) {
-    it.getPairedClients(resolve, reject);
-  }).then(function(/** Array<remoting.PairedClient> */ result) {
+  return it.getPairedClients().then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getPairedClients'
@@ -359,9 +325,7 @@
       type: 'clearPairedClientsResponse',
       result: deleted
     });
-    return new Promise(function (resolve, reject) {
-      it.clearPairedClients(resolve, reject);
-    }).then(function(/** * */ result) {
+    return it.clearPairedClients().then(function(result) {
       assert.deepEqual(postMessageStub.args[1][0], {
         id: 1,
         type: 'clearPairedClients'
@@ -378,9 +342,7 @@
       type: 'deletePairedClientResponse',
       result: deleted
     });
-    return new Promise(function (resolve, reject) {
-      it.deletePairedClient('<fakeClientId>', resolve, reject);
-    }).then(function(/** * */ result) {
+    return it.deletePairedClient('<fakeClientId>').then(function(result) {
       assert.deepEqual(postMessageStub.args[1][0], {
         id: 1,
         type: 'deletePairedClient',
@@ -397,9 +359,7 @@
     type: 'getHostClientIdResponse',
     clientId: '<fakeClientId>'
   });
-  return new Promise(function (resolve, reject) {
-    it.getHostClientId(resolve, reject);
-  }).then(function(/** * */ result) {
+  return it.getHostClientId().then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getHostClientId'
@@ -415,17 +375,17 @@
     userEmail: '<fakeUserEmail>',
     refreshToken: '<fakeRefreshToken>'
   });
-  return new Promise(function (resolve, reject) {
-    it.getCredentialsFromAuthCode(
-        '<fakeAuthCode>', resolveMulti(resolve), reject);
-  }).then(function(/** * */ result) {
+  return it.getCredentialsFromAuthCode('<fakeAuthCode>').then(function(result) {
     assert.deepEqual(postMessageStub.args[1][0], {
       id: 1,
       type: 'getCredentialsFromAuthCode',
       authorizationCode: '<fakeAuthCode>'
     });
-    assert.deepEqual(result, ['<fakeUserEmail>', '<fakeRefreshToken>']);
+    assert.deepEqual(result, {
+      userEmail: '<fakeUserEmail>',
+      refreshToken: '<fakeRefreshToken>'
+    });
   });
 });
 
-})();
\ No newline at end of file
+})();
diff --git a/remoting/webapp/crd/js/host_setup_dialog.js b/remoting/webapp/crd/js/host_setup_dialog.js
index 40cc7d5..df0b7e3 100644
--- a/remoting/webapp/crd/js/host_setup_dialog.js
+++ b/remoting/webapp/crd/js/host_setup_dialog.js
@@ -187,23 +187,20 @@
   var that = this;
 
   /**
-   * @param {boolean} supported True if crash dump reporting is supported by
-   *     the host.
-   * @param {boolean} allowed True if crash dump reporting is allowed.
-   * @param {boolean} set_by_policy True if crash dump reporting is controlled
-   *     by policy.
+   * @param {remoting.UsageStatsConsent} consent
    */
-  function onGetConsent(supported, allowed, set_by_policy) {
+  function onGetConsent(consent) {
     // Hide the usage stats check box if it is not supported or the policy
     // doesn't allow usage stats collection.
     var checkBoxLabel = that.usageStats_.querySelector('.checkbox-label');
-    that.usageStats_.hidden = !supported || (set_by_policy && !allowed);
-    that.usageStatsCheckbox_.checked = allowed;
+    that.usageStats_.hidden = !consent.supported ||
+        (consent.setByPolicy && !consent.allowed);
+    that.usageStatsCheckbox_.checked = consent.allowed;
 
-    that.usageStatsCheckbox_.disabled = set_by_policy;
-    checkBoxLabel.classList.toggle('disabled', set_by_policy);
+    that.usageStatsCheckbox_.disabled = consent.setByPolicy;
+    checkBoxLabel.classList.toggle('disabled', consent.setByPolicy);
 
-    if (set_by_policy) {
+    if (consent.setByPolicy) {
       that.usageStats_.title = l10n.getTranslationOrError(
           /*i18n-content*/ 'SETTING_MANAGED_BY_POLICY');
     } else {
@@ -223,7 +220,8 @@
   // known.
   this.usageStatsCheckbox_.disabled = true;
 
-  this.hostController_.getConsent(onGetConsent, onError);
+  this.hostController_.getConsent().then(
+      onGetConsent, remoting.Error.handler(onError));
 
   var flow = [
       remoting.HostSetupFlow.State.INSTALL_HOST,
@@ -427,8 +425,11 @@
     }
   }
 
-  this.hostController_.start(this.flow_.pin, this.flow_.consent, onHostStarted,
-                             onError);
+  this.hostController_.start(this.flow_.pin, this.flow_.consent).then(
+      onHostStarted
+  ).catch(
+      remoting.Error.handler(onError)
+  );
 };
 
 remoting.HostSetupDialog.prototype.updatePin_ = function() {
diff --git a/remoting/webapp/crd/js/it2me_activity.js b/remoting/webapp/crd/js/it2me_activity.js
index e6eac18..98b4bf5 100644
--- a/remoting/webapp/crd/js/it2me_activity.js
+++ b/remoting/webapp/crd/js/it2me_activity.js
@@ -15,13 +15,10 @@
 var ACCESS_CODE_LENGTH = SUPPORT_ID_LENGTH + HOST_SECRET_LENGTH;
 
 /**
- * @param {remoting.SessionConnector} sessionConnector
  * @constructor
  * @implements {remoting.Activity}
  */
-remoting.It2MeActivity = function(sessionConnector) {
-  /** @private */
-  this.sessionConnector_ = sessionConnector;
+remoting.It2MeActivity = function() {
   /** @private */
   this.hostId_ = '';
   /** @private */
@@ -34,9 +31,15 @@
     form,
     form.querySelector('#access-code-entry'),
     form.querySelector('#cancel-access-code-button'));
+
+  /** @private {remoting.DesktopRemotingActivity} */
+  this.desktopActivity_ = null;
 };
 
-remoting.It2MeActivity.prototype.dispose = function() {};
+remoting.It2MeActivity.prototype.dispose = function() {
+  base.dispose(this.desktopActivity_);
+  this.desktopActivity_ = null;
+};
 
 remoting.It2MeActivity.prototype.start = function() {
   var that = this;
@@ -51,10 +54,7 @@
   }).then(function(/** !remoting.Xhr.Response */ response) {
     return that.onHostInfo_(response);
   }).then(function(/** remoting.Host */ host) {
-    that.sessionConnector_.connect(
-        remoting.Application.Mode.IT2ME,
-        host,
-        new remoting.CredentialsProvider({ accessCode: that.passCode_ }));
+    that.connect_(host);
   }).catch(function(/** remoting.Error */ error) {
     if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
       remoting.setMode(remoting.AppMode.HOME);
@@ -93,6 +93,11 @@
   this.showFinishDialog_(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME);
 };
 
+/** @return {remoting.DesktopRemotingActivity} */
+remoting.It2MeActivity.prototype.getDesktopActivityForTesting = function() {
+  return this.desktopActivity_;
+};
+
 /**
  * @param {remoting.AppMode} mode
  * @private
@@ -170,6 +175,22 @@
 };
 
 /**
+ * @param {remoting.Host} host
+ * @private
+ */
+remoting.It2MeActivity.prototype.connect_ = function(host) {
+  base.dispose(this.desktopActivity_);
+  this.desktopActivity_ = new remoting.DesktopRemotingActivity(this);
+  var sessionConnector = remoting.SessionConnector.factory.createConnector(
+      document.getElementById('client-container'),
+      remoting.app_capabilities(),
+      this.desktopActivity_);
+  sessionConnector.connect(
+      remoting.Application.Mode.IT2ME, host,
+      new remoting.CredentialsProvider({ accessCode: this.passCode_ }));
+};
+
+/**
  * TODO(jrw): Replace with remoting.Error.fromHttpStatus.
  * @param {number} error An HTTP error code returned by the support-hosts
  *     endpoint.
diff --git a/remoting/webapp/crd/js/me2me_activity.js b/remoting/webapp/crd/js/me2me_activity.js
index 6ebf4ba..7dac1ed 100644
--- a/remoting/webapp/crd/js/me2me_activity.js
+++ b/remoting/webapp/crd/js/me2me_activity.js
@@ -10,18 +10,15 @@
 'use strict';
 
 /**
- * @param {remoting.SessionConnector} sessionConnector
  * @param {remoting.Host} host
  *
  * @constructor
  * @implements {remoting.Activity}
  */
-remoting.Me2MeActivity = function(sessionConnector, host) {
+remoting.Me2MeActivity = function(host) {
   /** @private */
   this.host_ = host;
   /** @private */
-  this.connector_ = sessionConnector;
-  /** @private */
   this.pinDialog_ =
       new remoting.PinDialog(document.getElementById('pin-dialog'), host);
   /** @private */
@@ -33,9 +30,15 @@
 
   /** @private {remoting.SmartReconnector} */
   this.reconnector_ = null;
+
+  /** @private {remoting.DesktopRemotingActivity} */
+  this.desktopActivity_ = null;
 };
 
-remoting.Me2MeActivity.prototype.dispose = function() {};
+remoting.Me2MeActivity.prototype.dispose = function() {
+  base.dispose(this.desktopActivity_);
+  this.desktopActivity_ = null;
+};
 
 remoting.Me2MeActivity.prototype.start = function() {
   var webappVersion = chrome.runtime.getManifest().version;
@@ -52,13 +55,36 @@
   });
 };
 
+/** @return {remoting.DesktopRemotingActivity} */
+remoting.Me2MeActivity.prototype.getDesktopActivity = function() {
+  return this.desktopActivity_;
+};
+
 /**
  * @param {boolean} suppressHostOfflineError
  * @private
  */
 remoting.Me2MeActivity.prototype.connect_ = function(suppressHostOfflineError) {
   remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
+  base.dispose(this.desktopActivity_);
+  this.desktopActivity_ = new remoting.DesktopRemotingActivity(this);
+  var connector = remoting.SessionConnector.factory.createConnector(
+        document.getElementById('client-container'),
+        remoting.app_capabilities(),
+        this.desktopActivity_);
+
+  connector.connect(
+      remoting.Application.Mode.ME2ME,
+      this.host_, this.createCredentialsProvider_(), suppressHostOfflineError);
+};
+
+/**
+ * @return {remoting.CredentialsProvider}
+ * @private
+ */
+remoting.Me2MeActivity.prototype.createCredentialsProvider_ = function() {
   var host = this.host_;
+  var that = this;
 
   /**
    * @param {string} tokenUrl Token-issue URL received from the host.
@@ -74,7 +100,6 @@
     thirdPartyTokenFetcher.fetchToken();
   };
 
-  var that = this;
   /**
    * @param {boolean} supportsPairing
    * @param {function(string):void} onPinFetched
@@ -89,17 +114,12 @@
     });
   };
 
-  var pairingInfo = /** @type{remoting.PairingInfo} */ (
-      base.deepCopy(host.options.pairingInfo));
-  var credentialsProvider = new remoting.CredentialsProvider({
+  return new remoting.CredentialsProvider({
     fetchPin: requestPin,
-    pairingInfo: pairingInfo,
+    pairingInfo: /** @type{remoting.PairingInfo} */ (
+        base.deepCopy(host.options.pairingInfo)),
     fetchThirdPartyToken: fetchThirdPartyToken
   });
-
-  this.connector_.connect(
-      remoting.Application.Mode.ME2ME,
-      host, credentialsProvider, suppressHostOfflineError);
 };
 
 /**
@@ -135,12 +155,12 @@
   this.retryOnHostOffline_ = true;
 
   var plugin = connectionInfo.plugin();
-  if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST)) {
+  var session = connectionInfo.session();
+  if (session.hasCapability(remoting.ClientSession.Capability.CAST)) {
     plugin.extensions().register(new remoting.CastExtensionHandler());
   }
   plugin.extensions().register(new remoting.GnubbyAuthHandler());
-  this.pinDialog_.requestPairingIfNecessary(connectionInfo.plugin(),
-                                            this.connector_);
+  this.pinDialog_.requestPairingIfNecessary(connectionInfo.plugin());
 
   base.dispose(this.reconnector_);
   this.reconnector_ = new remoting.SmartReconnector(
@@ -265,10 +285,8 @@
 
 /**
  * @param {remoting.ClientPlugin} plugin
- * @param {remoting.SessionConnector} connector
  */
-remoting.PinDialog.prototype.requestPairingIfNecessary =
-    function(plugin, connector) {
+remoting.PinDialog.prototype.requestPairingIfNecessary = function(plugin) {
   if (this.pairingCheckbox_.checked) {
     var that = this;
     /**
diff --git a/remoting/webapp/crd/js/mock_host_daemon_facade.js b/remoting/webapp/crd/js/mock_host_daemon_facade.js
index f7ee1ef..f404df6 100644
--- a/remoting/webapp/crd/js/mock_host_daemon_facade.js
+++ b/remoting/webapp/crd/js/mock_host_daemon_facade.js
@@ -87,29 +87,25 @@
 
 /**
  * @param {remoting.HostController.Feature} feature
- * @param {function(boolean):void} onDone
- * @return {boolean}
+ * @return {!Promise<boolean>}
  */
-remoting.MockHostDaemonFacade.prototype.hasFeature = function(feature, onDone) {
+remoting.MockHostDaemonFacade.prototype.hasFeature = function(feature) {
   var that = this;
-  Promise.resolve().then(function() {
-    onDone(that.features.indexOf(feature) >= 0);
+  return Promise.resolve().then(function() {
+    return that.features.indexOf(feature) >= 0;
   });
 };
 
 /**
- * @param {function(string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<string>}
  */
-remoting.MockHostDaemonFacade.prototype.getHostName =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getHostName = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.hostName === null) {
-      onError(remoting.Error.unexpected('getHostName'));
+      throw remoting.Error.unexpected('getHostName');
     } else {
-      onDone(that.hostName);
+      return that.hostName;
     }
   });
 };
@@ -117,116 +113,102 @@
 /**
  * @param {string} hostId
  * @param {string} pin
- * @param {function(string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<string>}
  */
-remoting.MockHostDaemonFacade.prototype.getPinHash =
-    function(hostId, pin, onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getPinHash = function(hostId, pin) {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.pinHashFunc === null) {
-      onError(remoting.Error.unexpected('getPinHash'));
+      throw remoting.Error.unexpected('getPinHash');
     } else {
-      onDone(that.pinHashFunc(hostId, pin));
+      return that.pinHashFunc(hostId, pin);
     }
   });
 };
 
 /**
- * @param {function(string, string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<{privateKey:string, publicKey:string}>}
  */
-remoting.MockHostDaemonFacade.prototype.generateKeyPair =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.generateKeyPair = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.privateKey === null || that.publicKey === null) {
-      onError(remoting.Error.unexpected('generateKeyPair'));
+      throw remoting.Error.unexpected('generateKeyPair');
     } else {
-      onDone(that.privateKey, that.publicKey);
+      return {
+        privateKey: that.privateKey,
+        publicKey: that.publicKey
+      };
     }
   });
 };
 
 /**
- * @param {Object} config
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @param {Object} config The new config parameters.
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
-remoting.MockHostDaemonFacade.prototype.updateDaemonConfig =
-    function(config, onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.updateDaemonConfig = function(config) {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.daemonConfig === null ||
         that.updateDaemonConfigResult === null ||
         'host_id' in config ||
         'xmpp_login' in config) {
-      onError(remoting.Error.unexpected('updateDaemonConfig'));
+      throw remoting.Error.unexpected('updateDaemonConfig');
     } else if (that.updateDaemonConfigResult !=
                remoting.HostController.AsyncResult.OK) {
-      onDone(that.updateDaemonConfigResult);
+      return that.updateDaemonConfigResult;
     } else {
       base.mix(that.daemonConfig, config);
-      onDone(remoting.HostController.AsyncResult.OK);
+      return remoting.HostController.AsyncResult.OK;
     }
   });
 };
 
 /**
- * @param {function(Object):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<Object>}
  */
-remoting.MockHostDaemonFacade.prototype.getDaemonConfig =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getDaemonConfig = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.daemonConfig === null) {
-      onError(remoting.Error.unexpected('getDaemonConfig'));
+      throw remoting.Error.unexpected('getDaemonConfig');
     } else {
-      onDone(that.daemonConfig);
+      return that.daemonConfig;
     }
   });
 };
 
 /**
- * @param {function(string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<string>}
  */
-remoting.MockHostDaemonFacade.prototype.getDaemonVersion =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getDaemonVersion = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.daemonVersion === null) {
-      onError(remoting.Error.unexpected('getDaemonVersion'));
+      throw remoting.Error.unexpected('getDaemonVersion');
     } else {
-      onDone(that.daemonVersion);
+      return that.daemonVersion;
     }
   });
 };
 
 /**
- * @param {function(boolean, boolean, boolean):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<remoting.UsageStatsConsent>}
  */
-remoting.MockHostDaemonFacade.prototype.getUsageStatsConsent =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getUsageStatsConsent = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.consentSupported === null ||
         that.consentAllowed === null ||
         that.consentSetByPolicy === null) {
-      onError(remoting.Error.unexpected('getUsageStatsConsent'));
+      throw remoting.Error.unexpected('getUsageStatsConsent');
     } else {
-      onDone(
-          that.consentSupported,
-          that.consentAllowed,
-          that.consentSetByPolicy);
+      return {
+        supported: that.consentSupported,
+        allowed: that.consentAllowed,
+        setByPolicy: that.consentSetByPolicy
+      };
     }
   });
 };
@@ -234,140 +216,127 @@
 /**
  * @param {Object} config
  * @param {boolean} consent Consent to report crash dumps.
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
 remoting.MockHostDaemonFacade.prototype.startDaemon =
-    function(config, consent, onDone, onError) {
+    function(config, consent) {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.startDaemonResult === null) {
-      onError(remoting.Error.unexpected('startDaemon'));
+      throw remoting.Error.unexpected('startDaemon');
     } else {
-      onDone(that.startDaemonResult);
+      return that.startDaemonResult;
     }
   });
 };
 
 /**
- * @param {function(remoting.HostController.AsyncResult):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<remoting.HostController.AsyncResult>}
  */
 remoting.MockHostDaemonFacade.prototype.stopDaemon =
-    function(onDone, onError) {
+    function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.stopDaemonResult === null) {
-      onError(remoting.Error.unexpected('stopDaemon'));
+      throw remoting.Error.unexpected('stopDaemon');
     } else {
-      onDone(that.stopDaemonResult);
+      return that.stopDaemonResult;
     }
   });
 };
 
 /**
- * @param {function(remoting.HostController.State):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<remoting.HostController.State>}
  */
 remoting.MockHostDaemonFacade.prototype.getDaemonState =
-    function(onDone, onError) {
+    function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.daemonState === null) {
-      onError(remoting.Error.unexpected('getDaemonState'));
+      throw remoting.Error.unexpected('getDaemonState');
     } else {
-      onDone(that.daemonState);
+      return that.daemonState;
     }
   });
 };
 
 /**
- * @param {function(Array<remoting.PairedClient>):void} onDone
- * @param {function(!remoting.Error):void} onError
+ * @return {!Promise<Array<remoting.PairedClient>>}
  */
 remoting.MockHostDaemonFacade.prototype.getPairedClients =
-    function(onDone, onError) {
+    function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.pairedClients === null) {
-      onError(remoting.Error.unexpected('getPairedClients'));
+      throw remoting.Error.unexpected('getPairedClients');
     } else {
-      onDone(that.pairedClients);
+      return that.pairedClients;
     }
   });
 };
 
 /**
- * @param {function(boolean):void} onDone
- * @param {function(!remoting.Error):void} onError
+ * @return {!Promise<boolean>}
  */
-remoting.MockHostDaemonFacade.prototype.clearPairedClients =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.clearPairedClients = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.pairedClients === null) {
-      onError(remoting.Error.unexpected('clearPairedClients'));
+      throw remoting.Error.unexpected('clearPairedClients');
     } else {
       that.pairedClients = [];
-      onDone(true);
+      return true;  // TODO(jrw): Not always correct.
     }
   });
 };
 
 /**
  * @param {string} client
- * @param {function(boolean):void} onDone
- * @param {function(!remoting.Error):void} onError
+ * @return {!Promise<boolean>}
  */
-remoting.MockHostDaemonFacade.prototype.deletePairedClient =
-    function(client, onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.deletePairedClient = function(client) {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.pairedClients === null) {
-      onError(remoting.Error.unexpected('deletePairedClient'));
+      throw remoting.Error.unexpected('deletePairedClient');
     } else {
-      that.pairedClients = that.pairedClients.filter(function(/** Object */ c) {
+      that.pairedClients = that.pairedClients.filter(function(c) {
         return c['clientId'] != client;
       });
-      onDone(true);
+      return true;  // TODO(jrw):  Not always correct.
     }
   });
 };
 
 /**
- * @param {function(string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<string>}
  */
-remoting.MockHostDaemonFacade.prototype.getHostClientId =
-    function(onDone, onError) {
+remoting.MockHostDaemonFacade.prototype.getHostClientId = function() {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.hostClientId === null) {
-      onError(remoting.Error.unexpected('getHostClientId'));
+      throw remoting.Error.unexpected('getHostClientId');
     } else {
-      onDone(that.hostClientId);
+      return that.hostClientId;
     }
   });
 };
 
 /**
  * @param {string} authorizationCode
- * @param {function(string, string):void} onDone
- * @param {function(!remoting.Error):void} onError
- * @return {void}
+ * @return {!Promise<{userEmail:string, refreshToken:string}>}
  */
 remoting.MockHostDaemonFacade.prototype.getCredentialsFromAuthCode =
-    function(authorizationCode, onDone, onError) {
+    function(authorizationCode) {
   var that = this;
-  Promise.resolve().then(function() {
+  return Promise.resolve().then(function() {
     if (that.userEmail === null || that.refreshToken === null) {
-      onError(remoting.Error.unexpected('getCredentialsFromAuthCode'));
+      throw remoting.Error.unexpected('getCredentialsFromAuthCode');
     } else {
-      onDone(that.userEmail, that.refreshToken);
+      return {
+        userEmail: that.userEmail,
+        refreshToken: that.refreshToken
+      };
     }
   });
 };
diff --git a/remoting/webapp/crd/js/session_connector.js b/remoting/webapp/crd/js/session_connector.js
index 7094c43..4481f5c 100644
--- a/remoting/webapp/crd/js/session_connector.js
+++ b/remoting/webapp/crd/js/session_connector.js
@@ -31,12 +31,6 @@
 remoting.SessionConnector.prototype.connect =
     function(mode, host, credentialsProvider, opt_suppressOfflineError) {};
 
-
-/**
- * Closes the session and removes the plugin element.
- */
-remoting.SessionConnector.prototype.closeSession = function() {};
-
 /**
  * @interface
  */
@@ -44,18 +38,13 @@
 
 /**
  * @param {HTMLElement} clientContainer Container element for the client view.
- * @param {function(remoting.ConnectionInfo):void} onConnected Callback on
- *     success.
- * @param {function(!remoting.Error):void} onError Callback on error.
- * @param {function(!remoting.Error):void} onConnectionFailed Callback for when
- *     the connection fails.
  * @param {Array<string>} requiredCapabilities Connector capabilities
  *     required by this application.
+ * @param {remoting.ClientSession.EventHandler} handler
  * @return {remoting.SessionConnector}
  */
 remoting.SessionConnectorFactory.prototype.createConnector =
-    function(clientContainer, onConnected, onError,
-             onConnectionFailed, requiredCapabilities) {};
+    function(clientContainer, requiredCapabilities, handler) {};
 
 /**
  * @type {remoting.SessionConnectorFactory}
diff --git a/remoting/webapp/crd/js/session_connector_impl.js b/remoting/webapp/crd/js/session_connector_impl.js
index 6bad8b3..6c197890 100644
--- a/remoting/webapp/crd/js/session_connector_impl.js
+++ b/remoting/webapp/crd/js/session_connector_impl.js
@@ -21,41 +21,36 @@
 
 /**
  * @param {HTMLElement} clientContainer Container element for the client view.
- * @param {function(remoting.ConnectionInfo):void} onConnected Callback on
- *     success.
- * @param {function(!remoting.Error):void} onError Callback on error.
- * @param {function(!remoting.Error):void} onConnectionFailed Callback for when
- *     the connection fails.
  * @param {Array<string>} requiredCapabilities Connector capabilities
  *     required by this application.
+ * @param {remoting.ClientSession.EventHandler} handler
  * @constructor
  * @implements {remoting.SessionConnector}
  */
-remoting.SessionConnectorImpl = function(clientContainer, onConnected, onError,
-                                         onConnectionFailed,
-                                         requiredCapabilities) {
+remoting.SessionConnectorImpl =
+    function(clientContainer, requiredCapabilities, handler) {
   /** @private {HTMLElement} */
   this.clientContainer_ = clientContainer;
 
-  /** @private {function(remoting.ConnectionInfo):void} */
-  this.onConnected_ = onConnected;
-
-  /** @private {function(!remoting.Error):void} */
-  this.onError_ = onError;
-
-  /** @private {function(!remoting.Error):void} */
-  this.onConnectionFailed_ = onConnectionFailed;
-
-  /** @private {Array<string>} */
-  this.requiredCapabilities_ = requiredCapabilities;
+  /** @private */
+  this.onError_ = handler.onError.bind(handler);
 
   /** @private */
-  this.bound_ = {
-    onStateChange : this.onStateChange_.bind(this)
-  };
+  this.handler_ = handler;
+
+  /** @private {Array<string>} */
+  this.requiredCapabilities_ = [
+    remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION,
+    remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS,
+    remoting.ClientSession.Capability.VIDEO_RECORDER
+  ];
+
+  // Append the app-specific capabilities.
+  this.requiredCapabilities_.push.apply(this.requiredCapabilities_,
+                                        requiredCapabilities);
 
   // Initialize/declare per-connection state.
-  this.closeSession();
+  this.closeSession_();
 };
 
 /**
@@ -63,10 +58,14 @@
  * second connection. Note the none of the shared WCS state is reset.
  * @private
  */
-remoting.SessionConnectorImpl.prototype.closeSession = function() {
+remoting.SessionConnectorImpl.prototype.closeSession_ = function() {
   // It's OK to initialize these member variables here because the
   // constructor calls this method.
 
+  base.dispose(this.eventHook_);
+  /** @private {base.Disposable} */
+  this.eventHook_ = null;
+
   /** @private {remoting.Host} */
   this.host_ = null;
 
@@ -106,7 +105,7 @@
   // in a new clientJid and a new callback. In this case, cancel any existing
   // connect operation and remove the old client plugin before instantiating a
   // new one.
-  this.closeSession();
+  this.closeSession_();
   remoting.app.setConnectionMode(mode);
   this.host_ = host;
   this.credentialsProvider_ = credentialsProvider;
@@ -213,10 +212,8 @@
   remoting.clientSession = this.clientSession_;
 
   this.clientSession_.logHostOfflineErrors(this.logHostOfflineErrors_);
-  this.clientSession_.addEventListener(
-      remoting.ClientSession.Events.stateChanged,
-      this.bound_.onStateChange);
-
+  this.eventHook_ = new base.EventHook(
+      this.clientSession_, 'stateChanged', this.onStateChange_.bind(this));
   this.plugin_.connect(
       this.host_, this.signalStrategy_.getJid(), this.credentialsProvider_);
 };
@@ -227,15 +224,11 @@
  */
 remoting.SessionConnectorImpl.prototype.pluginError_ = function(error) {
   this.signalStrategy_.setIncomingStanzaCallback(null);
-  this.clientSession_.disconnect(error);
-  this.closeSession();
+  this.closeSession_();
 };
 
 /**
- * Handle a change in the state of the client session prior to successful
- * connection (after connection, this class no longer handles state change
- * events). Errors that occur while connecting either trigger a reconnect
- * or notify the onError handler.
+ * Handle a change in the state of the client session.
  *
  * @param {remoting.ClientSession.StateEvent=} event
  * @return {void} Nothing.
@@ -244,25 +237,16 @@
 remoting.SessionConnectorImpl.prototype.onStateChange_ = function(event) {
   switch (event.current) {
     case remoting.ClientSession.State.CONNECTED:
-      // When the connection succeeds, deregister for state-change callbacks
-      // and pass the session to the onConnected callback. It is expected that
-      // it will register a new state-change callback to handle disconnect
-      // or error conditions.
-      this.clientSession_.removeEventListener(
-          remoting.ClientSession.Events.stateChanged,
-          this.bound_.onStateChange);
-
       var connectionInfo = new remoting.ConnectionInfo(
           this.host_, this.credentialsProvider_, this.clientSession_,
           this.plugin_);
-      this.onConnected_(connectionInfo);
+      this.handler_.onConnected(connectionInfo);
       break;
 
     case remoting.ClientSession.State.CONNECTING:
-      remoting.identity.getEmail().then(
-          function(/** string */ email) {
-            console.log('Connecting as ' + email);
-          });
+      remoting.identity.getEmail().then(function(/** string */ email) {
+        console.log('Connecting as ' + email);
+      });
       break;
 
     case remoting.ClientSession.State.AUTHENTICATED:
@@ -274,24 +258,14 @@
       break;
 
     case remoting.ClientSession.State.CLOSED:
-      // This class deregisters for state-change callbacks when the CONNECTED
-      // state is reached, so it only sees the CLOSED state in exceptional
-      // circumstances. For example, a CONNECTING -> CLOSED transition happens
-      // if the host closes the connection without an error message instead of
-      // accepting it. Since there's no way of knowing exactly what went wrong,
-      // we rely on server-side logs in this case and report a generic error
-      // message.
-      this.onError_(remoting.Error.unexpected());
+      this.handler_.onDisconnected();
       break;
 
     case remoting.ClientSession.State.FAILED:
       var error = this.clientSession_.getError();
       console.error('Client plugin reported connection failed: ' +
                     error.toString());
-      if (error == null) {
-        error = remoting.Error.unexpected();
-      }
-      this.onConnectionFailed_(error);
+      this.handler_.onConnectionFailed(error || remoting.Error.unexpected());
       break;
 
     default:
@@ -300,6 +274,10 @@
       // sync, and even then the version check should ensure compatibility.
       this.onError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN));
   }
+
+  if (this.clientSession_.isFinished()) {
+    this.closeSession_();
+  }
 };
 
 /**
@@ -310,20 +288,13 @@
 
 /**
  * @param {HTMLElement} clientContainer Container element for the client view.
- * @param {function(remoting.ConnectionInfo):void} onConnected Callback on
- *     success.
- * @param {function(!remoting.Error):void} onError Callback on error.
- * @param {function(!remoting.Error):void} onConnectionFailed Callback for when
- *     the connection fails.
  * @param {Array<string>} requiredCapabilities Connector capabilities
  *     required by this application.
+ * @param {remoting.ClientSession.EventHandler} handler
  * @return {remoting.SessionConnector}
  */
 remoting.DefaultSessionConnectorFactory.prototype.createConnector =
-    function(clientContainer, onConnected, onError,
-             onConnectionFailed, requiredCapabilities) {
-  return new remoting.SessionConnectorImpl(clientContainer, onConnected,
-                                           onError,
-                                           onConnectionFailed,
-                                           requiredCapabilities);
+    function(clientContainer, requiredCapabilities, handler) {
+  return new remoting.SessionConnectorImpl(clientContainer,
+                                           requiredCapabilities, handler);
 };
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 1794199..21f856b 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -290,11 +290,11 @@
   sources = [
     "system_headers/android_arm64_ucontext.h",
     "system_headers/android_arm_ucontext.h",
-    "system_headers/android_futex.h",
     "system_headers/android_i386_ucontext.h",
     "system_headers/android_ucontext.h",
     "system_headers/arm64_linux_syscalls.h",
     "system_headers/arm_linux_syscalls.h",
+    "system_headers/linux_futex.h",
     "system_headers/linux_seccomp.h",
     "system_headers/linux_syscalls.h",
     "system_headers/x86_32_linux_syscalls.h",
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 4305b41..1cf3b2d 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -280,13 +280,13 @@
       'sources': [
         'system_headers/android_arm64_ucontext.h',
         'system_headers/android_arm_ucontext.h',
-        'system_headers/android_futex.h',
         'system_headers/android_i386_ucontext.h',
         'system_headers/android_mips_ucontext.h',
         'system_headers/android_ucontext.h',
         'system_headers/arm64_linux_syscalls.h',
         'system_headers/arm_linux_syscalls.h',
         'system_headers/capability.h',
+        'system_headers/linux_futex.h',
         'system_headers/linux_seccomp.h',
         'system_headers/linux_syscalls.h',
         'system_headers/mips_linux_syscalls.h',
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index e6e89349..614849f 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/futex.h>
 #include <sched.h>
 #include <signal.h>
 #include <string.h>
@@ -32,7 +31,7 @@
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/services/thread_helpers.h"
-#include "sandbox/linux/system_headers/android_futex.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 #include "sandbox/linux/tests/test_utils.h"
 #include "sandbox/linux/tests/unit_tests.h"
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index b315f12a..282e727 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -7,7 +7,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <fcntl.h>
-#include <linux/futex.h>
 #include <linux/net.h>
 #include <sched.h>
 #include <signal.h>
@@ -30,12 +29,11 @@
 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 
 #if defined(OS_ANDROID)
 
-#include "sandbox/linux/system_headers/android_futex.h"
-
 #if !defined(F_DUPFD_CLOEXEC)
 #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
 #endif
diff --git a/sandbox/linux/system_headers/android_futex.h b/sandbox/linux/system_headers/linux_futex.h
similarity index 88%
rename from sandbox/linux/system_headers/android_futex.h
rename to sandbox/linux/system_headers/linux_futex.h
index 11b766f4..91733a8d 100644
--- a/sandbox/linux/system_headers/android_futex.h
+++ b/sandbox/linux/system_headers/linux_futex.h
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_FUTEX_H_
-#define SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_FUTEX_H_
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+
+#include <linux/futex.h>
 
 #if !defined(FUTEX_WAIT)
 #define FUTEX_WAIT 0
@@ -77,4 +79,4 @@
 #define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG)
 #endif
 
-#endif  // SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_FUTEX_H_
+#endif  // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 539bbd84..604cc25 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1,4 +1,14 @@
 {
+  "Linux ChromiumOS GN": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
   "Linux ChromiumOS Ozone Tests (1)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a91be73..b0903de4 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4,14 +4,14 @@
       {
         "args": [
           "--enable-browser-side-navigation",
-          "--gtest_filter=-BatteryMonitorImplTest.BatteryManagerDefaultValues:BatteryMonitorImplTest.BatteryManagerResolvePromise:BatteryMonitorImplTest.BatteryManagerWithEventListener:BookmarkletTest.DocumentWrite:BookmarkletTest.NonEmptyResult:BookmarkletTest.Redirect:BookmarkletTest.RedirectVoided:ChildProcessLauncherBrowserTest.ChildSpawnFail:CrossProcessFrameTreeBrowserTest.CreateCrossProcessSubframeProxies:CrossProcessFrameTreeBrowserTest.OriginSetOnCrossProcessNavigations:CrossSiteRedirectorBrowserTest.VerifyCrossSiteRedirectURL:CrossSiteTransferTest.NoLeakOnCrossSiteCancel:CrossSiteTransferTest.ReplaceEntryCrossProcessThenTransfer:CrossSiteTransferTest.ReplaceEntryCrossProcessTwice:CrossSiteTransferTest.ReplaceEntryInProcessThenTranfers:DatabaseTest.ReloadPage:DeviceInertialSensorBrowserTest.LightOneOffInfintyTest:DeviceInertialSensorBrowserTest.LightTest:DeviceInertialSensorBrowserTest.MotionNullTest:DeviceInertialSensorBrowserTest.MotionTest:DeviceInertialSensorBrowserTest.OrientationNullTest:DeviceInertialSensorBrowserTest.OrientationTest:DomSerializerTests.SerializeDocumentWithDownloadedIFrame:DOMStorageBrowserTest.SanityCheck:DOMStorageBrowserTest.SanityCheckIncognito:DownloadContentTest.CancelAtFinalRename:DownloadContentTest.CancelAtRelease:DownloadContentTest.CancelInterruptedDownload:DownloadContentTest.CancelResumingDownload:DownloadContentTest.DownloadCancelled:DownloadContentTest.DownloadGZipWithNoContent:DownloadContentTest.DownloadOctetStream:DownloadContentTest.MultiDownload:DownloadContentTest.RemoveDownload:DownloadContentTest.RemoveResumingDownload:DownloadContentTest.ResumeInterruptedDownload:DownloadContentTest.ResumeInterruptedDownloadBadPrecondition:DownloadContentTest.ResumeInterruptedDownloadNoRange:DownloadContentTest.ResumeInterruptedDownloadNoVerifiers:DownloadContentTest.ResumeWithDeletedFile:DownloadContentTest.ResumeWithFileFinalRenameError:DownloadContentTest.ResumeWithFileInitError:DownloadContentTest.ResumeWithFileIntermediateRenameError:DownloadContentTest.ShutdownAtRelease:DownloadContentTest.ShutdownInProgress:File/MediaTest.VideoBearOpusOgg/0:File/MediaTest.VideoBearOpusWebm/0:File/MediaTest.VideoBearSilentTheora/0:File/MediaTest.VideoBearSilentWebm/0:File/MediaTest.VideoBearTheora/0:File/MediaTest.VideoBearWavAlaw/0:File/MediaTest.VideoBearWavMulaw/0:File/MediaTest.VideoBearWavPcm/0:File/MediaTest.VideoBearWavPcm192kHz/0:File/MediaTest.VideoBearWavPcm3kHz/0:File/MediaTest.VideoBearWebm/0:File/MediaTest.VideoTulipWebm/0:FileSystemBrowserTest.CreateTest:FileSystemBrowserTest.RequestTest:FileSystemBrowserTestWithLowQuota.QuotaTest:FrameTreeBrowserTest.FrameTreeAfterCrash:FrameTreeBrowserTest.SandboxFlagsSetForChildFrames:GinBrowserTest.GinAndGarbageCollection:HostZoomMapImplBrowserTest.GetZoomForView_Host:HostZoomMapImplBrowserTest.GetZoomForView_HostAndScheme:IndexedDBBrowserTest.BlobsCountAgainstQuota:IndexedDBBrowserTest.CallbackAccounting:IndexedDBBrowserTest.CanDeleteWhenOverQuotaTest:IndexedDBBrowserTest.ConnectionsClosedOnTabClose:IndexedDBBrowserTest.CursorPrefetch:IndexedDBBrowserTest.CursorTest:IndexedDBBrowserTest.CursorTestIncognito:IndexedDBBrowserTest.DatabaseTest:IndexedDBBrowserTest.DeleteCompactsBackingStore:IndexedDBBrowserTest.DeleteForOriginDeletesBlobs:IndexedDBBrowserTest.DiskFullOnCommit:IndexedDBBrowserTest.DoesntHangTest:IndexedDBBrowserTest.EmptyBlob:IndexedDBBrowserTest.ForceCloseEventTest:IndexedDBBrowserTest.IndexTest:IndexedDBBrowserTest.KeyPathTest:IndexedDBBrowserTest.KeyTypesTest:IndexedDBBrowserTest.LevelDBLogFileTest:IndexedDBBrowserTest.NullKeyPathPersistence:IndexedDBBrowserTest.ObjectStoreTest:IndexedDBBrowserTest.PRE_NullKeyPathPersistence:IndexedDBBrowserTest.PRE_PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.TransactionGetTest:IndexedDBBrowserTest.TransactionTest:IndexedDBBrowserTest.VersionChangeCrashResilience:IndexedDBBrowserTestSingleProcess.RenderThreadShutdownTest:IndexedDBBrowserTestWithCorruptLevelDB.DestroyTest:IndexedDBBrowserTestWithGCExposed.BlobDidAck:IndexedDBBrowserTestWithGCExposed.BlobDidAckPrefetch:IndexedDBBrowserTestWithGCExposed.DatabaseCallbacksTest:IndexedDBBrowserTestWithLowQuota.QuotaTest:IndexedDBBrowserTestWithMissingSSTFile.DestroyTest:IndexedDBBrowserTestWithVersion0Schema.MigrationTest:IndexedDBBrowserTestWithVersion123456Schema.DestroyTest:IndexedDBBrowserTestWithVersion987654SSVData.DestroyTest:ManifestBrowserTest.DummyManifest:ManifestBrowserTest.DynamicManifest:ManifestBrowserTest.ParseErrorManifest:ManifestBrowserTest.ParsingErrorsManifest:MediaCanPlayTypeTest.CodecSupportTest_Avc1Variants:MediaCanPlayTypeTest.CodecSupportTest_Avc3Variants:MediaCanPlayTypeTest.CodecSupportTest_AvcLevels:MediaCanPlayTypeTest.CodecSupportTest_HLS:MediaCanPlayTypeTest.CodecSupportTest_mp3:MediaCanPlayTypeTest.CodecSupportTest_mp4:MediaCanPlayTypeTest.CodecSupportTest_Mp4aVariants:MediaCanPlayTypeTest.CodecSupportTest_ogg:MediaCanPlayTypeTest.CodecSupportTest_wav:MediaCanPlayTypeTest.CodecSupportTest_webm:MediaTest.Navigate:NavigationControllerBrowserTest.CorrectLengthWithCurrentItemReplacement:NavigationControllerBrowserTest.CorrectLengthWithNewTabNavigatingFromWebUI:NavigationControllerBrowserTest.DontIgnoreBackAfterNavEntryLimit:NavigationControllerBrowserTest.NavigationTypeClassification_ClientSideRedirect:NavigationControllerBrowserTest.NavigationTypeClassification_ExistingPage:NavigationControllerBrowserTest.NavigationTypeClassification_InPage:NavigationControllerBrowserTest.NavigationTypeClassification_NewAndAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_NewPage:NavigationControllerBrowserTest.SubframeOnEmptyPage:OutOfProcessPPAPITest.InputEvent:OutOfProcessPPAPITest.MediaStreamAudioTrack:OutOfProcessPPAPITest.MediaStreamVideoTrack:PluginPowerSaverHelperTest.ClearWhitelistOnNavigate:RendererAccessibilityTest.AccessibilityMessagesQueueWhileSwappedOut:RendererAccessibilityTest.DetachAccessibilityObject:RendererAccessibilityTest.EventOnObjectNotInTree:RendererAccessibilityTest.HideAccessibilityObject:RendererAccessibilityTest.SendFullAccessibilityTreeOnReload:RendererAccessibilityTest.ShowAccessibilityObject:RenderFrameHostImplBrowserTest.IsFocused_AtLoad:RenderFrameHostImplBrowserTest.IsFocused_Widget:RenderFrameHostManagerTest.BackForwardNotStale:RenderFrameHostManagerTest.ClickLinkAfter204Error:RenderFrameHostManagerTest.DisownSubframeOpener:RenderFrameHostManagerTest.DontPreemptNavigationWithFrameTreeUpdate:RenderFrameHostManagerTest.ForceSwapAfterWebUIBindings:RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed:RenderFrameHostManagerTest.NoScriptAccessAfterSwapOut:RenderFrameHostManagerTest.RendererDebugURLsDontSwap:RenderFrameHostManagerTest.RestoreFileAccessForHistoryNavigation:RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation:RenderFrameHostManagerTest.ShowLoadingURLUntilSpoof:RenderFrameImplTest.FrameResize:RenderFrameImplTest.FrameWasShown:RenderFrameImplTest.SubframeWidget:RenderProcessHostTest.AllProcessExitedCallsBeforeAnyHostDestroyedCalls:RenderViewBrowserTest.ConfirmCacheInformationPlumbed:RenderViewImplTest.ContextMenu:RenderViewImplTest.DecideNavigationPolicy:RenderViewImplTest.DidFailProvisionalLoadWithErrorForCancellation:RenderViewImplTest.FocusElementCallsFocusedNodeChanged:RenderViewImplTest.GetCompositionCharacterBoundsTest:RenderViewImplTest.GetSSLStatusOfFrame:RenderViewImplTest.ImeComposition:RenderViewImplTest.InsertCharacters:RenderViewImplTest.MessageOrderInDidChangeSelection:RenderViewImplTest.NavigateFrame:RenderViewImplTest.OnExtendSelectionAndDelete:RenderViewImplTest.OnHandleKeyboardEvent:RenderViewImplTest.OnImeTypeChanged:RenderViewImplTest.OnNavigationHttpPost:RenderViewImplTest.OnSetTextDirection:RenderViewImplTest.PreferredSizeZoomed:RenderViewImplTest.ReloadWhileSwappedOut:RenderViewImplTest.ScreenMetricsEmulation:RenderViewImplTest.SendCandidateWindowEvents:RenderViewImplTest.SendFaviconURLUpdateEvent:RenderViewImplTest.SendSwapOutACK:RenderViewImplTest.ServiceWorkerNetworkProviderSetup:RenderViewImplTest.SetEditableSelectionAndComposition:RenderViewImplTest.StaleNavigationsIgnored:RenderViewImplTest.TestBackForward:RenderViewImplTest.ZoomLimit:ResourceDispatcherHostBrowserTest.CrossSiteAfterCrash:ResourceDispatcherHostBrowserTest.CrossSiteFailedRequest:ResourceDispatcherHostBrowserTest.CrossSiteNavigationErrorPage2:ResourceDispatcherHostBrowserTest.CrossSiteNoUnloadOn204:ResourceFetcherTests.ResourceFetcher404:ResourceFetcherTests.ResourceFetcherDeletedInCallback:ResourceFetcherTests.ResourceFetcherDidFail:ResourceFetcherTests.ResourceFetcherDownload:ResourceFetcherTests.ResourceFetcherPost:ResourceFetcherTests.ResourceFetcherSetHeader:ResourceFetcherTests.ResourceFetcherTimeout:SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost:SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost:SecurityExploitBrowserTest.InterstitialCommandFromUnderlyingContent:ServiceWorkerBrowserTest.CrossSiteTransfer:ServiceWorkerBrowserTest.ImportsBustMemcache:ServiceWorkerBrowserTest.Reload:ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure:ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure:SessionHistoryTest.BasicBackForward:SessionHistoryTest.CrossFrameFormBackForward:SessionHistoryTest.FragmentBackForward:SessionHistoryTest.FrameBackForward:SessionHistoryTest.FrameFormBackForward:SessionHistoryTest.HistoryLength:SessionHistoryTest.JavascriptHistory:SessionHistoryTest.LocationChangeInSubframe:SessionHistoryTest.LocationReplace:SitePerProcessAccessibilityBrowserTest.CrossSiteIframeAccessibility:SitePerProcessBrowserTest.CompositorFrameSwapped:SitePerProcessBrowserTest.CreateProxiesForNewFrames:SitePerProcessBrowserTest.CrossSiteDidStopLoading:SitePerProcessBrowserTest.CrossSiteIframe:SitePerProcessBrowserTest.DynamicSandboxFlags:SitePerProcessBrowserTest.DynamicSandboxFlagsRemoteToLocal:SitePerProcessBrowserTest.KillingRendererClearsDescendantProxies:SitePerProcessBrowserTest.NavigateRemoteFrame:SitePerProcessBrowserTest.NavigateRemoteFrameToBlankAndDataURLs:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcessWithSubtree:SitePerProcessBrowserTest.NavigateWithSiblingRemoteFrame:SitePerProcessBrowserTest.OriginReplication:SitePerProcessBrowserTest.ProxyCreationSkipsSubtree:SitePerProcessBrowserTest.SandboxFlagsReplication:SitePerProcessDevToolsBrowserTest.CrossSiteIframeAgentHost:SpeechRecognitionBrowserTest.OneShotRecognition:StatsTableBrowserTest.StartWithStatTable:SuppressErrorPageTest.DoesNotSuppress:SuppressErrorPageTest.Suppresses:TracingControllerTest.EnableAndDisableRecording:TracingControllerTest.EnableAndDisableRecordingWithEmptyFileAndNullCallback:TracingControllerTest.EnableAndDisableRecordingWithFilePath:TracingControllerTest.EnableCaptureAndDisableMonitoring:TracingControllerTest.EnableCaptureAndDisableMonitoringWithEmptyFileAndNullCallback:TracingControllerTest.EnableCaptureAndDisableMonitoringWithFilePath:TracingControllerTest.GetCategories:TransitionBrowserTest.NormalNavigationNotDeferred:TransitionBrowserTest.TransitionNavigationDataIsCleared:TransitionBrowserTest.TransitionNavigationIsDeferred:WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail:WebContentsImplBrowserTest.GetSizeForNewRenderView:WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews:WebRtcAecDumpBrowserTest.CallWithAecDump:WebRtcAecDumpBrowserTest.CallWithAecDumpEnabledThenDisabled:WebRtcAecDumpBrowserTest.TwoCallsWithAecDump:WebUIMojoTest.EndToEndPing:WorkerTest.IncognitoSharedWorkers:WorkerTest.MultipleSharedWorkers:WorkerTest.MultipleWorkers:WorkerTest.PassMessagePortToSharedWorker:WorkerTest.PassMessagePortToSharedWorkerDontWaitForConnect:WorkerTest.SharedWorkerTlsClientAuth:WorkerTest.SingleSharedWorker:WorkerTest.SingleWorker:WorkerTest.WorkerTlsClientAuth"
+          "--gtest_filter=-BatteryMonitorImplTest.BatteryManagerDefaultValues:BatteryMonitorImplTest.BatteryManagerResolvePromise:BatteryMonitorImplTest.BatteryManagerWithEventListener:BookmarkletTest.DocumentWrite:BookmarkletTest.NonEmptyResult:BookmarkletTest.Redirect:BookmarkletTest.RedirectVoided:ChildProcessLauncherBrowserTest.ChildSpawnFail:CrossProcessFrameTreeBrowserTest.CreateCrossProcessSubframeProxies:CrossProcessFrameTreeBrowserTest.OriginSetOnCrossProcessNavigations:CrossSiteRedirectorBrowserTest.VerifyCrossSiteRedirectURL:CrossSiteTransferTest.NoLeakOnCrossSiteCancel:CrossSiteTransferTest.ReplaceEntryCrossProcessThenTransfer:CrossSiteTransferTest.ReplaceEntryCrossProcessTwice:CrossSiteTransferTest.ReplaceEntryInProcessThenTranfers:DatabaseTest.ReloadPage:DeviceInertialSensorBrowserTest.LightOneOffInfintyTest:DeviceInertialSensorBrowserTest.LightTest:DeviceInertialSensorBrowserTest.MotionNullTest:DeviceInertialSensorBrowserTest.MotionTest:DeviceInertialSensorBrowserTest.OrientationNullTest:DeviceInertialSensorBrowserTest.OrientationTest:DomSerializerTests.SerializeDocumentWithDownloadedIFrame:DOMStorageBrowserTest.SanityCheck:DOMStorageBrowserTest.SanityCheckIncognito:DownloadContentTest.CancelAtFinalRename:DownloadContentTest.CancelAtRelease:DownloadContentTest.CancelInterruptedDownload:DownloadContentTest.CancelResumingDownload:DownloadContentTest.DownloadCancelled:DownloadContentTest.DownloadGZipWithNoContent:DownloadContentTest.DownloadOctetStream:DownloadContentTest.MultiDownload:DownloadContentTest.RemoveDownload:DownloadContentTest.RemoveResumingDownload:DownloadContentTest.ResumeInterruptedDownload:DownloadContentTest.ResumeInterruptedDownloadBadPrecondition:DownloadContentTest.ResumeInterruptedDownloadNoRange:DownloadContentTest.ResumeInterruptedDownloadNoVerifiers:DownloadContentTest.ResumeWithDeletedFile:DownloadContentTest.ResumeWithFileFinalRenameError:DownloadContentTest.ResumeWithFileInitError:DownloadContentTest.ResumeWithFileIntermediateRenameError:DownloadContentTest.ShutdownAtRelease:DownloadContentTest.ShutdownInProgress:File/MediaTest.VideoBearOpusOgg/0:File/MediaTest.VideoBearOpusWebm/0:File/MediaTest.VideoBearSilentTheora/0:File/MediaTest.VideoBearSilentWebm/0:File/MediaTest.VideoBearTheora/0:File/MediaTest.VideoBearWavAlaw/0:File/MediaTest.VideoBearWavMulaw/0:File/MediaTest.VideoBearWavPcm/0:File/MediaTest.VideoBearWavPcm192kHz/0:File/MediaTest.VideoBearWavPcm3kHz/0:File/MediaTest.VideoBearWebm/0:File/MediaTest.VideoTulipWebm/0:FileSystemBrowserTest.CreateTest:FileSystemBrowserTest.RequestTest:FileSystemBrowserTestWithLowQuota.QuotaTest:FrameTreeBrowserTest.FrameTreeAfterCrash:FrameTreeBrowserTest.SandboxFlagsSetForChildFrames:GinBrowserTest.GinAndGarbageCollection:HostZoomMapImplBrowserTest.GetZoomForView_Host:HostZoomMapImplBrowserTest.GetZoomForView_HostAndScheme:IndexedDBBrowserTest.BlobsCountAgainstQuota:IndexedDBBrowserTest.CallbackAccounting:IndexedDBBrowserTest.CanDeleteWhenOverQuotaTest:IndexedDBBrowserTest.ConnectionsClosedOnTabClose:IndexedDBBrowserTest.CursorPrefetch:IndexedDBBrowserTest.CursorTest:IndexedDBBrowserTest.CursorTestIncognito:IndexedDBBrowserTest.DatabaseTest:IndexedDBBrowserTest.DeleteCompactsBackingStore:IndexedDBBrowserTest.DeleteForOriginDeletesBlobs:IndexedDBBrowserTest.DiskFullOnCommit:IndexedDBBrowserTest.DoesntHangTest:IndexedDBBrowserTest.EmptyBlob:IndexedDBBrowserTest.ForceCloseEventTest:IndexedDBBrowserTest.IndexTest:IndexedDBBrowserTest.KeyPathTest:IndexedDBBrowserTest.KeyTypesTest:IndexedDBBrowserTest.LevelDBLogFileTest:IndexedDBBrowserTest.NullKeyPathPersistence:IndexedDBBrowserTest.ObjectStoreTest:IndexedDBBrowserTest.PRE_NullKeyPathPersistence:IndexedDBBrowserTest.PRE_PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.PRE_VersionChangeCrashResilience:IndexedDBBrowserTest.TransactionGetTest:IndexedDBBrowserTest.TransactionTest:IndexedDBBrowserTest.VersionChangeCrashResilience:IndexedDBBrowserTestSingleProcess.RenderThreadShutdownTest:IndexedDBBrowserTestWithCorruptLevelDB.DestroyTest:IndexedDBBrowserTestWithGCExposed.BlobDidAck:IndexedDBBrowserTestWithGCExposed.BlobDidAckPrefetch:IndexedDBBrowserTestWithGCExposed.DatabaseCallbacksTest:IndexedDBBrowserTestWithLowQuota.QuotaTest:IndexedDBBrowserTestWithMissingSSTFile.DestroyTest:IndexedDBBrowserTestWithVersion0Schema.MigrationTest:IndexedDBBrowserTestWithVersion123456Schema.DestroyTest:IndexedDBBrowserTestWithVersion987654SSVData.DestroyTest:ManifestBrowserTest.DummyManifest:ManifestBrowserTest.DynamicManifest:ManifestBrowserTest.ParseErrorManifest:ManifestBrowserTest.ParsingErrorsManifest:MediaCanPlayTypeTest.CodecSupportTest_Avc1Variants:MediaCanPlayTypeTest.CodecSupportTest_Avc3Variants:MediaCanPlayTypeTest.CodecSupportTest_AvcLevels:MediaCanPlayTypeTest.CodecSupportTest_HLS:MediaCanPlayTypeTest.CodecSupportTest_mp3:MediaCanPlayTypeTest.CodecSupportTest_mp4:MediaCanPlayTypeTest.CodecSupportTest_Mp4aVariants:MediaCanPlayTypeTest.CodecSupportTest_ogg:MediaCanPlayTypeTest.CodecSupportTest_wav:MediaCanPlayTypeTest.CodecSupportTest_webm:MediaTest.Navigate:NavigationControllerBrowserTest.CorrectLengthWithCurrentItemReplacement:NavigationControllerBrowserTest.CorrectLengthWithNewTabNavigatingFromWebUI:NavigationControllerBrowserTest.DontIgnoreBackAfterNavEntryLimit:NavigationControllerBrowserTest.ErrorPageReplacement:NavigationControllerBrowserTest.NavigationTypeClassification_ClientSideRedirect:NavigationControllerBrowserTest.NavigationTypeClassification_ExistingPage:NavigationControllerBrowserTest.NavigationTypeClassification_InPage:NavigationControllerBrowserTest.NavigationTypeClassification_NewAndAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_NewPage:NavigationControllerBrowserTest.SubframeOnEmptyPage:OutOfProcessPPAPITest.InputEvent:OutOfProcessPPAPITest.MediaStreamAudioTrack:OutOfProcessPPAPITest.MediaStreamVideoTrack:PluginPowerSaverHelperTest.ClearWhitelistOnNavigate:RendererAccessibilityTest.AccessibilityMessagesQueueWhileSwappedOut:RendererAccessibilityTest.DetachAccessibilityObject:RendererAccessibilityTest.EventOnObjectNotInTree:RendererAccessibilityTest.HideAccessibilityObject:RendererAccessibilityTest.SendFullAccessibilityTreeOnReload:RendererAccessibilityTest.ShowAccessibilityObject:RenderFrameHostImplBrowserTest.IsFocused_AtLoad:RenderFrameHostImplBrowserTest.IsFocused_Widget:RenderFrameHostManagerTest.BackForwardNotStale:RenderFrameHostManagerTest.ClickLinkAfter204Error:RenderFrameHostManagerTest.DisownSubframeOpener:RenderFrameHostManagerTest.DontPreemptNavigationWithFrameTreeUpdate:RenderFrameHostManagerTest.ForceSwapAfterWebUIBindings:RenderFrameHostManagerTest.IgnoreRendererDebugURLsWhenCrashed:RenderFrameHostManagerTest.NoScriptAccessAfterSwapOut:RenderFrameHostManagerTest.RendererDebugURLsDontSwap:RenderFrameHostManagerTest.RestoreFileAccessForHistoryNavigation:RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation:RenderFrameHostManagerTest.ShowLoadingURLUntilSpoof:RenderProcessHostTest.AllProcessExitedCallsBeforeAnyHostDestroyedCalls:RenderViewBrowserTest.ConfirmCacheInformationPlumbed:RenderViewImplTest.DecideNavigationPolicy:RenderViewImplTest.InsertCharacters:RenderViewImplTest.NavigateFrame:RenderViewImplTest.OnHandleKeyboardEvent:RenderViewImplTest.OnNavigationHttpPost:RenderViewImplTest.ReloadWhileSwappedOut:RenderViewImplTest.StaleNavigationsIgnored:RenderViewImplTest.TestBackForward:ResourceDispatcherHostBrowserTest.CrossSiteAfterCrash:ResourceDispatcherHostBrowserTest.CrossSiteFailedRequest:ResourceDispatcherHostBrowserTest.CrossSiteNavigationErrorPage2:ResourceDispatcherHostBrowserTest.CrossSiteNoUnloadOn204:ResourceFetcherTests.ResourceFetcher404:ResourceFetcherTests.ResourceFetcherDeletedInCallback:ResourceFetcherTests.ResourceFetcherDidFail:ResourceFetcherTests.ResourceFetcherDownload:ResourceFetcherTests.ResourceFetcherPost:ResourceFetcherTests.ResourceFetcherSetHeader:ResourceFetcherTests.ResourceFetcherTimeout:SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost:SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost:SecurityExploitBrowserTest.InterstitialCommandFromUnderlyingContent:ServiceWorkerBrowserTest.CrossSiteTransfer:ServiceWorkerBrowserTest.ImportsBustMemcache:ServiceWorkerBrowserTest.Reload:ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure:ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure:SessionHistoryTest.BasicBackForward:SessionHistoryTest.CrossFrameFormBackForward:SessionHistoryTest.FragmentBackForward:SessionHistoryTest.FrameBackForward:SessionHistoryTest.FrameFormBackForward:SessionHistoryTest.HistoryLength:SessionHistoryTest.JavascriptHistory:SessionHistoryTest.LocationChangeInSubframe:SessionHistoryTest.LocationReplace:SitePerProcessAccessibilityBrowserTest.CrossSiteIframeAccessibility:SitePerProcessBrowserTest.CleanupCrossSiteIframe:SitePerProcessBrowserTest.CompositorFrameSwapped:SitePerProcessBrowserTest.CreateProxiesForNewFrames:SitePerProcessBrowserTest.CrossSiteDidStopLoading:SitePerProcessBrowserTest.CrossSiteIframe:SitePerProcessBrowserTest.DynamicSandboxFlags:SitePerProcessBrowserTest.DynamicSandboxFlagsRemoteToLocal:SitePerProcessBrowserTest.KillingRendererClearsDescendantProxies:SitePerProcessBrowserTest.NavigateRemoteFrame:SitePerProcessBrowserTest.NavigateRemoteFrameToBlankAndDataURLs:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess:SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcessWithSubtree:SitePerProcessBrowserTest.NavigateWithSiblingRemoteFrame:SitePerProcessBrowserTest.OriginReplication:SitePerProcessBrowserTest.ProxyCreationSkipsSubtree:SitePerProcessBrowserTest.RestrictFrameDetach:SitePerProcessBrowserTest.SandboxFlagsReplication:SitePerProcessBrowserTest.SubframePostMessage:SitePerProcessDevToolsBrowserTest.CrossSiteIframeAgentHost:SpeechRecognitionBrowserTest.OneShotRecognition:StatsTableBrowserTest.StartWithStatTable:TracingControllerTest.EnableAndDisableRecording:TracingControllerTest.EnableAndDisableRecordingWithEmptyFileAndNullCallback:TracingControllerTest.EnableAndDisableRecordingWithFilePath:TracingControllerTest.EnableCaptureAndDisableMonitoring:TracingControllerTest.EnableCaptureAndDisableMonitoringWithEmptyFileAndNullCallback:TracingControllerTest.EnableCaptureAndDisableMonitoringWithFilePath:TracingControllerTest.GetCategories:TransitionBrowserTest.NormalNavigationNotDeferred:TransitionBrowserTest.TransitionNavigationDataIsCleared:TransitionBrowserTest.TransitionNavigationIsDeferred:WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail:WebContentsImplBrowserTest.GetSizeForNewRenderView:WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews:WebRtcAecDumpBrowserTest.CallWithAecDump:WebRtcAecDumpBrowserTest.CallWithAecDumpEnabledThenDisabled:WebRtcAecDumpBrowserTest.TwoCallsWithAecDump:WebUIMojoTest.EndToEndPing:WorkerTest.IncognitoSharedWorkers:WorkerTest.MultipleSharedWorkers:WorkerTest.MultipleWorkers:WorkerTest.PassMessagePortToSharedWorker:WorkerTest.PassMessagePortToSharedWorkerDontWaitForConnect:WorkerTest.SharedWorkerTlsClientAuth:WorkerTest.SingleSharedWorker:WorkerTest.SingleWorker:WorkerTest.WorkerTlsClientAuth"
         ],
         "test": "content_browsertests"
       },
       {
         "args": [
           "--enable-browser-side-navigation",
-          "--gtest_filter=-DevToolsManagerTest.ReattachOnCancelPendingNavigation:NavigationControllerTest.CopyRestoredStateAndNavigate:NavigationControllerTest.LoadURL_AbortDoesntCancelPending:NavigationControllerTest.LoadURL_IgnorePreemptsPending:NavigationControllerTest.ReloadOriginalRequestURL:NavigationControllerTest.ShowRendererURLAfterFailUntilModified:NavigationControllerTest.ShowRendererURLInNewTabUntilModified:OverscrollNavigationOverlayTest.LoadUpdateWithoutNonEmptyPaint:RenderFrameHostManagerTest.AlwaysSendEnableViewSourceMode:RenderFrameHostManagerTest.CancelPendingProperlyDeletesOrSwaps:RenderFrameHostManagerTest.DetachPendingChild:RenderFrameHostManagerTest.PageDoesBackAndReload:RenderViewHostTest.ResetUnloadOnReload:ResourceDispatcherHostTest.TransferNavigationHtml:ResourceDispatcherHostTest.TransferNavigationText:ResourceDispatcherHostTest.TransferNavigationWithProcessCrash:ResourceDispatcherHostTest.TransferNavigationWithTwoRedirects:ResourceDispatcherHostTest.TransferTwoNavigationsHtml:WebContentsImplTest.ActiveContentsCountChangeBrowsingInstance:WebContentsImplTest.CrossSiteNavigationBackPreempted:WebContentsImplTest.CrossSiteNavigationCanceled:WebContentsImplTest.CrossSiteNavigationPreempted:WebContentsImplTest.CrossSiteNotPreemptedDuringBeforeUnload:WebContentsImplTest.NoEarlyStop"
+          "--gtest_filter=-NavigationControllerTest.CopyRestoredStateAndNavigate:NavigationControllerTest.LoadURL_AbortDoesntCancelPending:NavigationControllerTest.LoadURL_IgnorePreemptsPending:NavigationControllerTest.ShowRendererURLAfterFailUntilModified:NavigationControllerTest.ShowRendererURLInNewTabUntilModified:OverscrollNavigationOverlayTest.LoadUpdateWithoutNonEmptyPaint:RenderFrameHostManagerTest.CancelPendingProperlyDeletesOrSwaps:RenderFrameHostManagerTest.DetachPendingChild:RenderFrameHostManagerTest.PageDoesBackAndReload:RenderViewHostTest.ResetUnloadOnReload:ResourceDispatcherHostTest.TransferNavigationHtml:ResourceDispatcherHostTest.TransferNavigationText:ResourceDispatcherHostTest.TransferNavigationWithProcessCrash:ResourceDispatcherHostTest.TransferNavigationWithTwoRedirects:ResourceDispatcherHostTest.TransferTwoNavigationsHtml:WebContentsImplTest.CrossSiteNavigationBackPreempted:WebContentsImplTest.CrossSiteNavigationPreempted:WebContentsImplTest.CrossSiteNotPreemptedDuringBeforeUnload:WebContentsImplTest.NoEarlyStop"
         ],
         "test": "content_unittests"
       }
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 60a9e4c..2738e38 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1,4 +1,14 @@
 {
+  "Android GN": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
   "Android Tests": {
     "scripts": [
       {
@@ -100,6 +110,26 @@
       }
     ]
   },
+  "Linux GN": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "Linux GN (dbg)": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
   "Linux Tests": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 89ff4e2..2907a38 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -1,4 +1,24 @@
 {
+  "Mac GN": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "Mac GN (dbg)": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
   "Mac10.6 Tests": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 582f21c..002e188 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -983,6 +983,26 @@
       }
     ]
   },
+  "Win8 GN": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "Win8 GN (dbg)": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
   "XP Tests (1)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/tryserver.chromium.linux.json b/testing/buildbot/tryserver.chromium.linux.json
index 0967ef4..7ffe2c3e 100644
--- a/testing/buildbot/tryserver.chromium.linux.json
+++ b/testing/buildbot/tryserver.chromium.linux.json
@@ -1 +1,62 @@
-{}
+{
+  "android_chromium_gn_compile_dbg": {
+    "additional_compile_targets": [
+      "chrome_shell_apk"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "android_chromium_gn_compile_rel": {
+    "additional_compile_targets": [
+      "chrome_shell_apk"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "linux_chromium_gn_chromeos_dbg": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "linux_chromium_gn_chromeos_rel": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "linux_chromium_gn_dbg": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "linux_chromium_gn_rel": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  }
+}
diff --git a/testing/buildbot/tryserver.chromium.mac.json b/testing/buildbot/tryserver.chromium.mac.json
index 66724b36..3e49ba2 100644
--- a/testing/buildbot/tryserver.chromium.mac.json
+++ b/testing/buildbot/tryserver.chromium.mac.json
@@ -1,7 +1,17 @@
 {
   "mac_chromium_gn_dbg": {
     "additional_compile_targets": [
-      "gn_all"
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "mac_chromium_gn_rel": {
+    "additional_compile_targets": [
+      "all"
     ],
     "gtest_tests": [
       {
diff --git a/testing/buildbot/tryserver.chromium.win.json b/testing/buildbot/tryserver.chromium.win.json
index 0967ef4..60650fa2 100644
--- a/testing/buildbot/tryserver.chromium.win.json
+++ b/testing/buildbot/tryserver.chromium.win.json
@@ -1 +1,22 @@
-{}
+{
+  "win8_chromium_gn_dbg": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  },
+  "win8_chromium_gn_rel": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "gtest_tests": [
+      {
+        "test": "base_unittests"
+      }
+    ]
+  }
+}
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium
index 368f9e8..ccab606 100644
--- a/third_party/libjingle/README.chromium
+++ b/third_party/libjingle/README.chromium
@@ -1,7 +1,7 @@
 Name: libjingle
 URL: http://code.google.com/p/webrtc/
 Version: unknown
-Revision: 8975
+Revision: 8990
 License: BSD
 License File: source/talk/COPYING
 Security Critical: yes
diff --git a/third_party/protobuf/README.chromium b/third_party/protobuf/README.chromium
index c6bb5c2..e894d79 100644
--- a/third_party/protobuf/README.chromium
+++ b/third_party/protobuf/README.chromium
@@ -40,3 +40,5 @@
 in binary size.
 
 A BUILD.gn file has been added for building with GN.
+
+Cherry-pick pherl changes to make protobuf build on VS2015.
diff --git a/third_party/protobuf/src/google/protobuf/stubs/hash.h b/third_party/protobuf/src/google/protobuf/stubs/hash.h
index f7d1071..82d5052e 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/hash.h
+++ b/third_party/protobuf/src/google/protobuf/stubs/hash.h
@@ -1,6 +1,6 @@
 // Protocol Buffers - Google's data interchange format
 // Copyright 2008 Google Inc.  All rights reserved.
-// http://code.google.com/p/protobuf/
+// https://developers.google.com/protocol-buffers/
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
@@ -37,13 +37,14 @@
 
 #include <string.h>
 #include <google/protobuf/stubs/common.h>
-#include "config.h"
+#include <google/protobuf/stubs/pbconfig.h>
 
-#if defined(HAVE_HASH_MAP) && defined(HAVE_HASH_SET)
-#include HASH_MAP_H
-#include HASH_SET_H
+#if defined(GOOGLE_PROTOBUF_HAVE_HASH_MAP) && \
+    defined(GOOGLE_PROTOBUF_HAVE_HASH_SET)
+#include GOOGLE_PROTOBUF_HASH_MAP_H
+#include GOOGLE_PROTOBUF_HASH_SET_H
 #else
-#define MISSING_HASH
+#define GOOGLE_PROTOBUF_MISSING_HASH
 #include <map>
 #include <set>
 #endif
@@ -51,7 +52,7 @@
 namespace google {
 namespace protobuf {
 
-#ifdef MISSING_HASH
+#ifdef GOOGLE_PROTOBUF_MISSING_HASH
 
 // This system doesn't have hash_map or hash_set.  Emulate them using map and
 // set.
@@ -88,15 +89,17 @@
 
 template <typename Key, typename Data,
           typename HashFcn = hash<Key>,
-          typename EqualKey = int >
-class hash_map : public std::map<Key, Data, HashFcn> {
+          typename EqualKey = std::equal_to<Key>,
+          typename Alloc = std::allocator< std::pair<const Key, Data> > >
+class hash_map : public std::map<Key, Data, HashFcn, EqualKey, Alloc> {
  public:
-  hash_map(int = 0) {}
+  hash_map(int = 0, const HashFcn& = HashFcn(), const EqualKey& = EqualKey(),
+           const Alloc& = Alloc()) {}
 };
 
 template <typename Key,
           typename HashFcn = hash<Key>,
-          typename EqualKey = int >
+          typename EqualKey = std::equal_to<Key> >
 class hash_set : public std::set<Key, HashFcn> {
  public:
   hash_set(int = 0) {}
@@ -105,7 +108,7 @@
 #elif defined(_MSC_VER) && !defined(_STLPORT_VERSION)
 
 template <typename Key>
-struct hash : public HASH_NAMESPACE::hash_compare<Key> {
+struct hash : public GOOGLE_PROTOBUF_HASH_NAMESPACE::hash_compare<Key> {
 };
 
 // MSVC's hash_compare<const char*> hashes based on the string contents but
@@ -119,23 +122,26 @@
 
 template <>
 struct hash<const char*>
-  : public HASH_NAMESPACE::hash_compare<const char*, CstringLess> {
-};
+    : public GOOGLE_PROTOBUF_HASH_NAMESPACE::hash_compare<
+        const char*, CstringLess> {};
 
 template <typename Key, typename Data,
           typename HashFcn = hash<Key>,
-          typename EqualKey = int >
-class hash_map : public HASH_NAMESPACE::hash_map<
-    Key, Data, HashFcn> {
+          typename EqualKey = std::equal_to<Key>,
+          typename Alloc = std::allocator< std::pair<const Key, Data> > >
+class hash_map
+    : public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
+          Key, Data, HashFcn, EqualKey, Alloc> {
  public:
-  hash_map(int = 0) {}
+  hash_map(int = 0, const HashFcn& = HashFcn(), const EqualKey& = EqualKey(),
+           const Alloc& = Alloc()) {}
 };
 
-template <typename Key,
-          typename HashFcn = hash<Key>,
-          typename EqualKey = int >
-class hash_set : public HASH_NAMESPACE::hash_set<
-    Key, HashFcn> {
+template <typename Key, typename HashFcn = hash<Key>,
+          typename EqualKey = std::equal_to<Key> >
+class hash_set
+    : public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_SET_CLASS<
+          Key, HashFcn, EqualKey> {
  public:
   hash_set(int = 0) {}
 };
@@ -143,7 +149,7 @@
 #else
 
 template <typename Key>
-struct hash : public HASH_NAMESPACE::hash<Key> {
+struct hash : public GOOGLE_PROTOBUF_HASH_NAMESPACE::hash<Key> {
 };
 
 template <typename Key>
@@ -168,23 +174,27 @@
 
 template <typename Key, typename Data,
           typename HashFcn = hash<Key>,
-          typename EqualKey = std::equal_to<Key> >
-class hash_map : public HASH_NAMESPACE::HASH_MAP_CLASS<
-    Key, Data, HashFcn, EqualKey> {
+          typename EqualKey = std::equal_to<Key>,
+          typename Alloc = std::allocator< std::pair<const Key, Data> > >
+class hash_map
+    : public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_MAP_CLASS<
+          Key, Data, HashFcn, EqualKey, Alloc> {
  public:
-  hash_map(int = 0) {}
+  hash_map(int = 0, const HashFcn& = HashFcn(), const EqualKey& = EqualKey(),
+           const Alloc& = Alloc()) {}
 };
 
-template <typename Key,
-          typename HashFcn = hash<Key>,
+template <typename Key, typename HashFcn = hash<Key>,
           typename EqualKey = std::equal_to<Key> >
-class hash_set : public HASH_NAMESPACE::HASH_SET_CLASS<
-    Key, HashFcn, EqualKey> {
+class hash_set
+    : public GOOGLE_PROTOBUF_HASH_NAMESPACE::GOOGLE_PROTOBUF_HASH_SET_CLASS<
+          Key, HashFcn, EqualKey> {
  public:
   hash_set(int = 0) {}
 };
 
-#endif
+#undef GOOGLE_PROTOBUF_MISSING_HASH
+#endif  // !GOOGLE_PROTOBUF_MISSING_HASH
 
 template <>
 struct hash<string> {
diff --git a/third_party/protobuf/src/google/protobuf/stubs/pbconfig.h b/third_party/protobuf/src/google/protobuf/stubs/pbconfig.h
new file mode 100644
index 0000000..1c0cfbe
--- /dev/null
+++ b/third_party/protobuf/src/google/protobuf/stubs/pbconfig.h
@@ -0,0 +1,74 @@
+/* Modified for Chromium to support stlport and libc++ adaptively */
+/* protobuf config.h for MSVC.  On other platforms, this is generated
+ * automatically by autoheader / autoconf / configure. */
+
+// NOTE: if you add new macros in this file manually, please propagate the macro
+// to vsprojects/config.h.
+
+/* the namespace of hash_map/hash_set */
+// Apparently Microsoft decided to move hash_map *back* to the std namespace
+// in MSVC 2010:
+//   http://blogs.msdn.com/vcblog/archive/2009/05/25/stl-breaking-changes-in-visual-studio-2010-beta-1.aspx
+// And.. they are moved back to stdext in MSVC 2013 (haven't checked 2012). That
+// said, use unordered_map for MSVC 2010 and beyond is our safest bet.
+#if _MSC_VER >= 1600
+#define GOOGLE_PROTOBUF_HASH_NAMESPACE std
+#define GOOGLE_PROTOBUF_HASH_MAP_H <unordered_map>
+#define GOOGLE_PROTOBUF_HASH_MAP_CLASS unordered_map
+#define GOOGLE_PROTOBUF_HASH_SET_H <unordered_set>
+#define GOOGLE_PROTOBUF_HASH_SET_CLASS unordered_set
+#elif _MSC_VER >= 1310
+#define GOOGLE_PROTOBUF_HASH_NAMESPACE stdext
+#define GOOGLE_PROTOBUF_HASH_MAP_H <hash_map>
+#define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
+#define GOOGLE_PROTOBUF_HASH_SET_H <hash_set>
+#define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
+#else
+/* the name of <hash_map> */
+#if defined(_LIBCPP_VERSION)
+#define GOOGLE_PROTOBUF_HASH_MAP_CLASS unordered_map
+#else
+#define GOOGLE_PROTOBUF_HASH_MAP_CLASS hash_map
+#endif
+
+/* the location of <unordered_map> or <hash_map> */
+#if defined(USE_STLPORT)
+#define GOOGLE_PROTOBUF_HASH_MAP_H <hash_map>
+#elif defined(_LIBCPP_VERSION)
+#define GOOGLE_PROTOBUF_HASH_MAP_H <unordered_map>
+#else
+#define GOOGLE_PROTOBUF_HASH_MAP_H <ext/hash_map>
+#endif
+
+/* the namespace of hash_map/hash_set */
+#if defined(USE_STLPORT) || defined(_LIBCPP_VERSION)
+#define GOOGLE_PROTOBUF_HASH_NAMESPACE std
+#else
+#define GOOGLE_PROTOBUF_HASH_NAMESPACE __gnu_cxx
+#endif
+
+/* the name of <hash_set> */
+#if defined(_LIBCPP_VERSION)
+#define GOOGLE_PROTOBUF_HASH_SET_CLASS unordered_set
+#else
+#define GOOGLE_PROTOBUF_HASH_SET_CLASS hash_set
+#endif
+
+/* the location of <unordered_set> or <hash_set> */
+#if defined(USE_STLPORT)
+#define GOOGLE_PROTOBUF_HASH_SET_H <hash_set>
+#elif defined(_LIBCPP_VERSION)
+#define GOOGLE_PROTOBUF_HASH_SET_H <unordered_set>
+#else
+#define GOOGLE_PROTOBUF_HASH_SET_H <ext/hash_set>
+#endif
+
+#endif  // _MSC_VER >= 1600
+
+/* the location of <hash_set> */
+
+/* define if the compiler has hash_map */
+#define GOOGLE_PROTOBUF_HAVE_HASH_MAP 1
+
+/* define if the compiler has hash_set */
+#define GOOGLE_PROTOBUF_HAVE_HASH_SET 1
diff --git a/third_party/protobuf/vsprojects/config.h b/third_party/protobuf/vsprojects/config.h
index 2c64450a..a93bb033 100644
--- a/third_party/protobuf/vsprojects/config.h
+++ b/third_party/protobuf/vsprojects/config.h
@@ -1,28 +1,19 @@
 /* protobuf config.h for MSVC.  On other platforms, this is generated
  * automatically by autoheader / autoconf / configure. */
 
-/* the location of <hash_map> */
-#define HASH_MAP_H <hash_map>
+#include <google/protobuf/stubs/pbconfig.h>
 
-/* the namespace of hash_map/hash_set */
-// Apparently Microsoft decided to move hash_map *back* to the std namespace
-// in MSVC 2010:
-//   http://blogs.msdn.com/vcblog/archive/2009/05/25/stl-breaking-changes-in-visual-studio-2010-beta-1.aspx
-// TODO(kenton):  Use unordered_map instead, which is available in MSVC 2010.
-#if _MSC_VER < 1310 || _MSC_VER >= 1600
-#define HASH_NAMESPACE std
-#else
-#define HASH_NAMESPACE stdext
+#define HASH_MAP_H GOOGLE_PROTOBUF_HASH_MAP_H
+#define HASH_NAMESPACE GOOGLE_PROTOBUF_HASH_NAMESPACE
+#define HASH_SET_H GOOGLE_PROTOBUF_HASH_SET_H
+
+#ifdef GOOGLE_PROTOBUF_HAVE_HASH_MAP
+#define HAVE_HASH_MAP GOOGLE_PROTOBUF_HAVE_HASH_MAP
 #endif
 
-/* the location of <hash_set> */
-#define HASH_SET_H <hash_set>
-
-/* define if the compiler has hash_map */
-#define HAVE_HASH_MAP 1
-
-/* define if the compiler has hash_set */
-#define HAVE_HASH_SET 1
+#ifdef GOOGLE_PROTOBUF_HAVE_HASH_SET
+#define HAVE_HASH_SET GOOGLE_PROTOBUF_HAVE_HASH_SET
+#endif
 
 /* define if you want to use zlib.  See readme.txt for additional
  * requirements. */
diff --git a/tools/chrome_proxy/integration_tests/chrome_proxy_pagesets/client_type.py b/tools/chrome_proxy/integration_tests/chrome_proxy_pagesets/client_type.py
index 83bc903..bd70d5b 100644
--- a/tools/chrome_proxy/integration_tests/chrome_proxy_pagesets/client_type.py
+++ b/tools/chrome_proxy/integration_tests/chrome_proxy_pagesets/client_type.py
@@ -39,36 +39,36 @@
 
     # Page that should cause a bypass for android chrome clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=ANDROID',
+        url='http://check.googlezip.net/chrome-proxy-header/c_android',
         page_set=self,
         bypass_for_client_type='android'))
 
     # Page that should cause a bypass for android webview clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=WEBVIEW',
+        url='http://check.googlezip.net/chrome-proxy-header/c_webview',
         page_set=self,
         bypass_for_client_type='webview'))
 
     # Page that should cause a bypass for iOS clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=IOS',
+        url='http://check.googlezip.net/chrome-proxy-header/c_ios',
         page_set=self,
         bypass_for_client_type='ios'))
 
     # Page that should cause a bypass for Linux clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=LINUX',
+        url='http://check.googlezip.net/chrome-proxy-header/c_linux',
         page_set=self,
         bypass_for_client_type='linux'))
 
     # Page that should cause a bypass for Windows clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=WIN',
+        url='http://check.googlezip.net/chrome-proxy-header/c_win',
         page_set=self,
         bypass_for_client_type='win'))
 
     # Page that should cause a bypass for ChromeOS clients.
     self.AddUserStory(ClientTypePage(
-        url='http://check.googlezip.net/chrome-proxy-header/c=CHROMEOS',
+        url='http://check.googlezip.net/chrome-proxy-header/c_chromeos',
         page_set=self,
         bypass_for_client_type='chromeos'))
diff --git a/tools/cygprofile/profile_android_startup.py b/tools/cygprofile/profile_android_startup.py
index b23d2db7..f483dddf 100644
--- a/tools/cygprofile/profile_android_startup.py
+++ b/tools/cygprofile/profile_android_startup.py
@@ -14,16 +14,26 @@
 import shutil
 import subprocess
 import sys
+import tempfile
 import time
 
 sys.path.append(os.path.join(sys.path[0], '..', '..', 'build', 'android'))
 from pylib import android_commands
 from pylib import constants
 from pylib import flag_changer
+from pylib import forwarder
 from pylib.device import device_errors
 from pylib.device import device_utils
 from pylib.device import intent
 
+sys.path.append(os.path.join(sys.path[0], '..', '..', 'tools', 'telemetry'))
+from telemetry.core import webpagereplay
+
+sys.path.append(os.path.join(sys.path[0], '..', '..',
+    'third_party', 'webpagereplay'))
+import adb_install_cert
+import certutils
+
 
 class NoCyglogDataError(Exception):
   """An error used to indicate that no cyglog data was collected."""
@@ -36,15 +46,153 @@
     return repr(self.value)
 
 
+def _DownloadFromCloudStorage(bucket, sha1_file_name):
+  """Download the given file based on a hash file."""
+  cmd = ['download_from_google_storage', '--no_resume',
+         '--bucket', bucket, '-s', sha1_file_name]
+  print 'Executing command ' + ' '.join(cmd)
+  process = subprocess.Popen(cmd)
+  process.wait()
+  if process.returncode != 0:
+    raise Exception('Exception executing command %s' % ' '.join(cmd))
+
+
+class WprManager(object):
+  """A utility to download a WPR archive, host it, and forward device ports to
+  it.
+  """
+
+  _WPR_BUCKET = 'chrome-partner-telemetry'
+
+  def __init__(self, wpr_archive, device, cmdline_file):
+    self._device = device
+    self._wpr_archive = wpr_archive
+    self._wpr_archive_hash = wpr_archive + '.sha1'
+    self._cmdline_file = cmdline_file
+    self._wpr_server = None
+    self._wpr_ca_cert_path = None
+    self._device_cert_util = None
+    self._host_http_port = None
+    self._host_https_port = None
+    self._is_test_ca_installed = False
+    self._flag_changer = None
+
+  def Start(self):
+    """Set up the device and host for WPR."""
+    self.Stop()
+    self._InstallTestCa()
+    self._BringUpWpr()
+    self._StartForwarder()
+
+  def Stop(self):
+    """Clean up the device and host's WPR setup."""
+    self._StopForwarder()
+    self._StopWpr()
+    self._RemoveTestCa()
+
+  def __enter__(self):
+    self.Start()
+
+  def __exit__(self, unused_exc_type, unused_exc_val, unused_exc_tb):
+    self.Stop()
+
+  def _InstallTestCa(self):
+    """Generates and deploys a test certificate authority."""
+    print 'Installing test certificate authority on device: %s' % (
+        self._device.adb.GetDeviceSerial())
+    self._wpr_ca_cert_path = os.path.join(tempfile.mkdtemp(), 'testca.pem')
+    certutils.write_dummy_ca_cert(*certutils.generate_dummy_ca_cert(),
+                                  cert_path=self._wpr_ca_cert_path)
+    self._device_cert_util = adb_install_cert.AndroidCertInstaller(
+        self._device.adb.GetDeviceSerial(), None, self._wpr_ca_cert_path)
+    self._device_cert_util.install_cert(overwrite_cert=True)
+    self._is_test_ca_installed = True
+
+  def _RemoveTestCa(self):
+    """Remove root CA generated by previous call to InstallTestCa().
+
+    Removes the test root certificate from both the device and host machine.
+    """
+    print 'Cleaning up test CA...'
+    if not self._wpr_ca_cert_path:
+      return
+
+    if self._is_test_ca_installed:
+      try:
+        self._device_cert_util.remove_cert()
+      except Exception:
+        # Best effort cleanup - show the error and continue.
+        logging.error(
+            'Error while trying to remove certificate authority: %s. '
+            % self._adb.device_serial())
+      self._is_test_ca_installed = False
+
+    shutil.rmtree(os.path.dirname(self._wpr_ca_cert_path), ignore_errors=True)
+    self._wpr_ca_cert_path = None
+    self._device_cert_util = None
+
+  def _BringUpWpr(self):
+    """Start the WPR server on the host and the forwarder on the device."""
+    print 'Starting WPR on host...'
+    _DownloadFromCloudStorage(self._WPR_BUCKET, self._wpr_archive_hash)
+    self._wpr_server = webpagereplay.ReplayServer(self._wpr_archive,
+        '127.0.0.1', 0, 0, None,
+        ['--should_generate_certs',
+         '--https_root_ca_cert_path=' + self._wpr_ca_cert_path,
+         '--use_closest_match'])
+    ports = self._wpr_server.StartServer()[:-1]
+    self._host_http_port = ports[0]
+    self._host_https_port = ports[1]
+
+  def _StopWpr(self):
+    """ Stop the WPR and forwarder. """
+    print 'Stopping WPR on host...'
+    if self._wpr_server:
+      self._wpr_server.StopServer()
+
+  def _StartForwarder(self):
+    """Sets up forwarding of device ports to the host, and configures chrome
+    to use those ports.
+    """
+    print 'Starting device forwarder...'
+    forwarder.Forwarder.Map([(0, self._host_http_port),
+                             (0, self._host_https_port)],
+                            self._device)
+    device_http = forwarder.Forwarder.DevicePortForHostPort(
+        self._host_http_port)
+    device_https = forwarder.Forwarder.DevicePortForHostPort(
+        self._host_https_port)
+    self._flag_changer = flag_changer.FlagChanger(
+        self._device, self._cmdline_file)
+    self._flag_changer.AddFlags([
+        '--host-resolver-rules="MAP * 127.0.0.1,EXCLUDE localhost"',
+        '--testing-fixed-http-port=%s' % device_http,
+        '--testing-fixed-https-port=%s' % device_https])
+
+  def _StopForwarder(self):
+    """Shuts down the port forwarding service."""
+    print 'Stopping device forwarder...'
+    if self._flag_changer:
+      self._flag_changer.Restore()
+      self._flag_changer = None
+    forwarder.Forwarder.UnmapAllDevicePorts(self._device)
+
+
 class AndroidProfileTool(object):
-  """ A utility for generating cygprofile data for chrome on andorid.
+  """A utility for generating cygprofile data for chrome on andorid.
 
   Runs cygprofile_unittest found in output_directory, does profiling runs,
   and pulls the data to the local machine in output_directory/cyglog_data.
   """
 
   _DEVICE_CYGLOG_DIR = '/data/local/tmp/chrome/cyglog'
+
+  # TEST_URL must be a url in the WPR_ARCHIVE.
   _TEST_URL = 'https://www.google.com/#hl=en&q=science'
+  _WPR_ARCHIVE = os.path.join(
+      constants.DIR_SOURCE_ROOT, 'tools', 'perf', 'page_sets', 'data',
+      'top_10_mobile_002.wpr')
+
 
   def __init__(self, output_directory):
     devices = android_commands.GetAttachedDevices()
@@ -56,7 +204,7 @@
     self._SetUpDevice()
 
   def RunCygprofileTests(self):
-    """ Run the cygprofile unit tests suite on the device.
+    """Run the cygprofile unit tests suite on the device.
 
     Args:
       path_to_tests: The location on the host machine with the compiled
@@ -73,7 +221,7 @@
     return exit_code
 
   def CollectProfile(self, apk, package_info):
-    """ Run a profile and collect the log files.
+    """Run a profile and collect the log files.
 
     Args:
       apk: The location of the chrome apk to profile.
@@ -85,25 +233,31 @@
       NoCyglogDataError: No data was found on the device.
     """
     self._Install(apk, package_info)
-    self._SetChromeFlags(package_info)
-    self._SetUpDeviceFolders()
-    # Start up chrome once with a blank page, just to get the one-off
-    # activities out of the way such as apk resource extraction and profile
-    # creation.
-    self._StartChrome(package_info, 'about:blank')
-    time.sleep(15)
-    self._KillChrome(package_info)
-    self._SetUpDeviceFolders()
-    # TODO(azarchs): Set up WPR, device forwarding, test CA.
-    self._StartChrome(package_info, self._TEST_URL)
-    time.sleep(60)
-    self._KillChrome(package_info)
+
+    try:
+      changer = self._SetChromeFlags(package_info)
+      self._SetUpDeviceFolders()
+      # Start up chrome once with a blank page, just to get the one-off
+      # activities out of the way such as apk resource extraction and profile
+      # creation.
+      self._StartChrome(package_info, 'about:blank')
+      time.sleep(15)
+      self._KillChrome(package_info)
+      self._SetUpDeviceFolders()
+      with WprManager(self._WPR_ARCHIVE, self._device,
+                      package_info.cmdline_file):
+        self._StartChrome(package_info, self._TEST_URL)
+        time.sleep(90)
+        self._KillChrome(package_info)
+    finally:
+      self._RestoreChromeFlags(changer)
+
     data = self._PullCyglogData()
     self._DeleteDeviceData()
     return data
 
   def Cleanup(self):
-    """ Delete all local and device files left over from profiling. """
+    """Delete all local and device files left over from profiling. """
     self._DeleteDeviceData()
     self._DeleteHostData()
 
@@ -118,7 +272,7 @@
     self._device.old_interface.ManagedInstall(apk, package_info.package)
 
   def _SetUpDevice(self):
-    """ When profiling, files are output to the disk by every process.  This
+    """When profiling, files are output to the disk by every process.  This
     means running without sandboxing enabled.
     """
     # We need to have adb root in order to pull cyglog data
@@ -135,22 +289,26 @@
       logging.error(str(e))
 
   def _SetChromeFlags(self, package_info):
-    print 'Setting flags...'
+    print 'Setting Chrome flags...'
     changer = flag_changer.FlagChanger(
         self._device, package_info.cmdline_file)
     changer.AddFlags(['--no-sandbox', '--disable-fre'])
-    # TODO(azarchs): backup and restore flags.
-    logging.warning('Chrome flags changed and will not be restored.')
+    return changer
+
+  def _RestoreChromeFlags(self, changer):
+    print 'Restoring Chrome flags...'
+    if changer:
+      changer.Restore()
 
   def _SetUpDeviceFolders(self):
-    """ Creates folders on the device to store cyglog data. """
+    """Creates folders on the device to store cyglog data. """
     print 'Setting up device folders...'
     self._DeleteDeviceData()
     self._device.old_interface.RunShellCommand(
         'mkdir -p %s' % self._DEVICE_CYGLOG_DIR)
 
   def _DeleteDeviceData(self):
-    """ Clears out cyglog storage locations on the device. """
+    """Clears out cyglog storage locations on the device. """
     self._device.old_interface.RunShellCommand(
         'rm -rf %s' % self._DEVICE_CYGLOG_DIR)
 
@@ -167,9 +325,7 @@
     self._device.KillAll(package_info.package)
 
   def _DeleteHostData(self):
-    """
-    Clears out cyglog storage locations on the host.
-    """
+    """Clears out cyglog storage locations on the host."""
     shutil.rmtree(self._host_cyglog_dir, ignore_errors=True)
 
   def _SetUpHostFolders(self):
@@ -177,7 +333,7 @@
     os.mkdir(self._host_cyglog_dir)
 
   def _PullCyglogData(self):
-    """ Pull the cyglog data off of the device.
+    """Pull the cyglog data off of the device.
 
     Returns:
       A list of cyglog data files which were pulled.
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index e34efaac..832f53a 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -34,20 +34,23 @@
 }
 
 // Forward declaration for function below.
-void RecursivePrintTargetDeps(const DepMap& dep_map,
-                              const Target* target,
-                              TargetSet* seen_targets,
-                              int indent_level);
+size_t RecursivePrintTargetDeps(const DepMap& dep_map,
+                               const Target* target,
+                               TargetSet* seen_targets,
+                               int indent_level);
 
 // Prints the target and its dependencies in tree form. If the set is non-null,
 // new targets encountered will be added to the set, and if a ref is in the set
 // already, it will not be recused into. When the set is null, all refs will be
 // printed.
-void RecursivePrintTarget(const DepMap& dep_map,
+//
+// Returns the number of items printed.
+size_t RecursivePrintTarget(const DepMap& dep_map,
                           const Target* target,
                           TargetSet* seen_targets,
                           int indent_level) {
   std::string indent(indent_level * 2, ' ');
+  size_t count = 1;
 
   // Only print the toolchain for non-default-toolchain targets.
   OutputString(indent + target->label().GetUserVisibleName(
@@ -69,22 +72,28 @@
   }
 
   OutputString("\n");
-  if (print_children)
-    RecursivePrintTargetDeps(dep_map, target, seen_targets, indent_level + 1);
+  if (print_children) {
+    count += RecursivePrintTargetDeps(dep_map, target, seen_targets,
+                                      indent_level + 1);
+  }
+  return count;
 }
 
 // Prints refs of the given target (not the target itself). See
 // RecursivePrintTarget.
-void RecursivePrintTargetDeps(const DepMap& dep_map,
+size_t RecursivePrintTargetDeps(const DepMap& dep_map,
                               const Target* target,
                               TargetSet* seen_targets,
                               int indent_level) {
   DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
   DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+  size_t count = 0;
   for (DepMap::const_iterator cur_dep = dep_begin;
        cur_dep != dep_end; cur_dep++) {
-    RecursivePrintTarget(dep_map, cur_dep->second, seen_targets, indent_level);
+    count += RecursivePrintTarget(dep_map, cur_dep->second, seen_targets,
+                                  indent_level);
   }
+  return count;
 }
 
 void RecursiveCollectChildRefs(const DepMap& dep_map,
@@ -179,30 +188,35 @@
   }
 }
 
-void DoTreeOutput(const DepMap& dep_map,
-                  const UniqueVector<const Target*>& implicit_target_matches,
-                  const UniqueVector<const Target*>& explicit_target_matches,
-                  bool all) {
+// Returns the number of matches printed.
+size_t DoTreeOutput(const DepMap& dep_map,
+                    const UniqueVector<const Target*>& implicit_target_matches,
+                    const UniqueVector<const Target*>& explicit_target_matches,
+                    bool all) {
   TargetSet seen_targets;
+  size_t count = 0;
 
   // Implicit targets don't get printed themselves.
   for (const Target* target : implicit_target_matches) {
     if (all)
-      RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
+      count += RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
     else
-      RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
+      count += RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
   }
 
   // Explicit targets appear in the output.
   for (const Target* target : implicit_target_matches) {
     if (all)
-      RecursivePrintTarget(dep_map, target, nullptr, 0);
+      count += RecursivePrintTarget(dep_map, target, nullptr, 0);
     else
-      RecursivePrintTarget(dep_map, target, &seen_targets, 0);
+      count += RecursivePrintTarget(dep_map, target, &seen_targets, 0);
   }
+
+  return count;
 }
 
-void DoAllListOutput(
+// Returns the number of matches printed.
+size_t DoAllListOutput(
     const DepMap& dep_map,
     const UniqueVector<const Target*>& implicit_target_matches,
     const UniqueVector<const Target*>& explicit_target_matches) {
@@ -218,9 +232,11 @@
   }
 
   FilterAndPrintTargetSet(false, results);
+  return results.size();
 }
 
-void DoDirectListOutput(
+// Returns the number of matches printed.
+size_t DoDirectListOutput(
     const DepMap& dep_map,
     const UniqueVector<const Target*>& implicit_target_matches,
     const UniqueVector<const Target*>& explicit_target_matches) {
@@ -241,6 +257,7 @@
     results.insert(target);
 
   FilterAndPrintTargetSet(false, results);
+  return results.size();
 }
 
 }  // namespace
@@ -294,6 +311,12 @@
     "\n"
     TARGET_PRINTING_MODE_COMMAND_LINE_HELP
     "\n"
+    "  -q\n"
+    "     Quiet. If nothing matches, don't print any output. Without this\n"
+    "     option, if there are no matches there will be an informational\n"
+    "     message printed which might interfere with scripts processing the\n"
+    "     output.\n"
+    "\n"
     TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
     "\n"
     "  --tree\n"
@@ -347,9 +370,9 @@
     "      potentially affected by a change to the given file.\n";
 
 int RunRefs(const std::vector<std::string>& args) {
-  if (args.size() != 2) {
+  if (args.size() <= 1) {
     Err(Location(), "You're holding it wrong.",
-        "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)\"")
+        "Usage: \"gn refs <out_dir> (<label_pattern>|<file>)*\"")
         .PrintToStdout();
     return 1;
   }
@@ -394,16 +417,35 @@
                                 &explicit_target_matches);
   }
 
+  // Tell the user if their input matches no files or labels. We need to check
+  // both that it matched no targets and no configs. File input will already
+  // have been converted to targets at this point. Configs will have been
+  // converted to targets also, but there could be no targets referencing the
+  // config, which is different than no config with that name.
+  bool quiet = cmdline->HasSwitch("q");
+  if (!quiet && config_matches.empty() &&
+      explicit_target_matches.empty() && target_matches.empty()) {
+    OutputString("The input matches no targets, configs, or files.\n",
+                 DECORATION_YELLOW);
+    return 1;
+  }
+
   // Construct the reverse dependency tree.
   DepMap dep_map;
   FillDepMap(setup, &dep_map);
 
+  size_t cnt = 0;
   if (tree)
-    DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
+    cnt = DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
   else if (all)
-    DoAllListOutput(dep_map, target_matches, explicit_target_matches);
+    cnt = DoAllListOutput(dep_map, target_matches, explicit_target_matches);
   else
-    DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
+    cnt = DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
+
+  // If you ask for the references of a valid target, but that target has
+  // nothing referencing it, we'll get here without having printed anything.
+  if (!quiet && cnt == 0)
+    OutputString("Nothing references this.\n", DECORATION_YELLOW);
 
   return 0;
 }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 6f26aed6..e9a28ec 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -147,10 +147,10 @@
       'Win8 GN (dbg)': 'gn_debug_bot',
     },
     'tryserver.chromium.linux': {
-      'android_chromium_gn_dbg': 'android_gn_debug_bot',
-      'android_chromium_gn_rel': 'android_gn_release_trybot',
-      'linux_chromiumos_chromium_gn_rel': 'chromeos_gn_release_trybot', 
-      'linux_chromiumos_chromium_gn_dbg': 'chromeos_gn_debug_bot',
+      'android_chromium_gn_compile_dbg': 'android_gn_debug_bot',
+      'android_chromium_gn_compile_rel': 'android_gn_release_trybot',
+      'linux_chromium_gn_chromeos_rel': 'chromeos_gn_release_trybot',
+      'linux_chromium_gn_chromeos_dbg': 'chromeos_gn_debug_bot',
       'linux_chromium_gn_dbg': 'gn_debug_bot', 
       'linux_chromium_gn_rel': 'gn_release_trybot', 
       'linux_chromium_gn_upload_x64': 'gn_release_bot',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8a0cd77..4d4e3ca 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -11306,7 +11306,7 @@
   </summary>
 </histogram>
 
-<histogram name="GPU.InitializeOneOffMediumTime" units="microseconds">
+<histogram name="GPU.InitializeOneOffMediumTime" units="milliseconds">
   <owner>jmadill@chromium.org</owner>
   <summary>
     The time that the GPU process spends in initializing the GL surface, and
@@ -23290,6 +23290,14 @@
   </summary>
 </histogram>
 
+<histogram name="Network.Shill.Wifi.SignalAtDisconnect" units="negative dBm">
+  <owner>silberst@chromium.org</owner>
+  <summary>
+    Chrome OS network metric indicating the negative of the dBm received signal
+    strength recorded at the time of a WiFi disconnect.
+  </summary>
+</histogram>
+
 <histogram name="Network.Shill.Wifi.SignalStrength" units="negative dBm">
   <owner>quiche@chromium.org</owner>
   <summary>
@@ -34666,24 +34674,48 @@
   </summary>
 </histogram>
 
-<histogram name="ServiceWorker.StartWorker.Status"
-    units="ServiceWorkerStatusCode">
+<histogram name="ServiceWorker.StartNewWorker.Status"
+    enum="ServiceWorkerStatusCode">
   <owner>falken@chromium.org</owner>
-  <summary>The result of trying to start a Service Worker.</summary>
+  <summary>
+    The result of trying to start a Service Worker that has not yet installed.
+    See also ServiceWorker.StartWorker.Status for installed workers.
+  </summary>
+</histogram>
+
+<histogram name="ServiceWorker.StartNewWorker.Time" units="milliseconds">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    The time taken to start a Service Worker that has not yet installed, from
+    process allocation to ACK of started from the renderer (which occurs after
+    script execution). This may include script download time. The metric is not
+    recorded if DevTools was ever attached to the Service Worker during startup.
+    See also ServiceWorker.StartWorker.Time for installed workers.
+  </summary>
+</histogram>
+
+<histogram name="ServiceWorker.StartWorker.Status"
+    enum="ServiceWorkerStatusCode">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    The result of trying to start a Service Worker that is already installed.
+    See also ServiceWorker.StartNewWorker.Status for new workers.
+  </summary>
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.Time" units="milliseconds">
   <owner>falken@chromium.org</owner>
   <summary>
-    The time taken to start a Service Worker end-to-end, from process allocation
-    to ACK of started from the renderer. This possibly includes script download
-    time, but the metric is not recorded if DevTools was ever attached to the
-    Service Worker during startup.
+    The time taken to start a Service Worker that is already installed, from
+    process allocation to ACK of started from the renderer (which occurs after
+    script execution). The metric is not recorded if DevTools was ever attached
+    to the Service Worker during startup. See also
+    ServiceWorker.StartNewWorker.Time for new workers.
   </summary>
 </histogram>
 
 <histogram name="ServiceWorker.StartWorker.TimeoutPhase"
-    units="EmbeddedWorkerStartingPhase">
+    enum="EmbeddedWorkerStartingPhase">
   <owner>falken@chromium.org</owner>
   <summary>
     The phase the EmbeddedWorker was in when ServiceWorker startup timed out.
@@ -38684,6 +38716,22 @@
   </summary>
 </histogram>
 
+<histogram name="Startup.AfterStartupTaskCount">
+  <owner>michaeln@chromium.org</owner>
+  <summary>
+    The number of after-startup tasks that were queued prior to startup
+    completion and deferred until that time.
+  </summary>
+</histogram>
+
+<histogram name="Startup.AfterStartupTaskDelayedUntilTime" units="milliseconds">
+  <owner>michaeln@chromium.org</owner>
+  <summary>
+    Time from the process creation until deferred after-startup tasks began
+    running.
+  </summary>
+</histogram>
+
 <histogram name="Startup.AppListFirstPaintColdStart" units="milliseconds">
   <owner>tapted@chromium.org</owner>
   <summary>
@@ -40924,6 +40972,42 @@
   </summary>
 </histogram>
 
+<histogram name="Tabs.TabCountActiveWindow" units="Tabs">
+  <owner>bruthig@chromium.org</owner>
+  <owner>tdanderson@chromium.org</owner>
+  <summary>
+    The number of tabs open in the active window when a load completes.
+  </summary>
+</histogram>
+
+<histogram name="Tabs.TabCountPerLoad" units="Tabs">
+  <owner>bruthig@chromium.org</owner>
+  <owner>tdanderson@chromium.org</owner>
+  <summary>
+    The number of tabs open in all browsers (counting app-mode windows) when a
+    load completes.
+  </summary>
+  <details>
+    This is basically the average number of tabs over time.
+
+    See also MPArch.RPHCountPerLoad for the number of processes used by these
+    tabs.
+  </details>
+</histogram>
+
+<histogram name="Tabs.TabCountPerWindow" units="Tabs">
+  <owner>bruthig@chromium.org</owner>
+  <owner>tdanderson@chromium.org</owner>
+  <summary>
+    The number of tabs open per window (counting app-mode windows) when a load
+    completes.
+  </summary>
+  <details>
+    This value will be recorded multiple times per load if more than one window
+    is open.
+  </details>
+</histogram>
+
 <histogram name="ThreadWatcher.ResponseTime" units="milliseconds">
   <owner>rtenneti@chromium.org</owner>
   <summary>
@@ -45069,6 +45153,44 @@
   </summary>
 </histogram>
 
+<histogram name="WindowManager.AppWindowCountPerLoad">
+  <owner>kuscher@chromium.org</owner>
+  <summary>
+    The number of app windows open when a load completes. This includes windows
+    opened by an app shortcut, or apps opened in a popup. This only counts v1
+    apps.
+  </summary>
+</histogram>
+
+<histogram name="WindowManager.PanelWindowCountPerLoad">
+  <obsolete>
+    Deprecated 4/2013. No longer tracked.
+  </obsolete>
+  <owner>kuscher@chromium.org</owner>
+  <summary>
+    The number of panel windows open when a load completes. Panels are windows
+    docked to the bottom of the OS desktop, which are visible to the user even
+    while the user is interacting with other applications.
+  </summary>
+</histogram>
+
+<histogram name="WindowManager.PopUpWindowCountPerLoad">
+  <owner>kuscher@chromium.org</owner>
+  <summary>
+    The number of popup windows open when a load completes. Popup windows only
+    have one content area (no multiple tabs) and a stripped down toolbar
+    consisting only of a read-only address bar.
+  </summary>
+</histogram>
+
+<histogram name="WindowManager.TabbedWindowCountPerLoad">
+  <owner>kuscher@chromium.org</owner>
+  <summary>
+    The number of tabbed windows open when a load completes. A tabbed window is
+    a normal browser window which can have one or more tabs.
+  </summary>
+</histogram>
+
 <histogram name="Windows.Tablet" enum="BooleanTablet">
   <owner>zturner@chromium.org</owner>
   <summary>Count of browser launches from a Windows tablet pc.</summary>
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
index b0f7d60c..ef5ed07 100644
--- a/tools/perf/benchmarks/dromaeo.py
+++ b/tools/perf/benchmarks/dromaeo.py
@@ -212,6 +212,7 @@
     return 'dromaeo.jslibeventjquery'
 
 
+@benchmark.Disabled('android', 'linux')  # http://crbug.com/476592
 class DromaeoJslibEventPrototype(_DromaeoBenchmark):
   """Dromaeo JSLib event prototype JavaScript benchmark.
 
diff --git a/tools/perf/benchmarks/oilpan_gc_times.py b/tools/perf/benchmarks/oilpan_gc_times.py
index 6f55e1e..f1577f9 100644
--- a/tools/perf/benchmarks/oilpan_gc_times.py
+++ b/tools/perf/benchmarks/oilpan_gc_times.py
@@ -5,7 +5,6 @@
 import os
 
 from telemetry import benchmark
-from telemetry.core import util
 from telemetry import page
 
 from benchmarks import blink_perf
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index 01aae239..0110866 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -51,7 +51,7 @@
     return 'smoothness.tough_canvas_cases'
 
 
-@benchmark.Disabled('android', 'mac')  # crbug.com/373812
+@benchmark.Disabled('android')  # crbug.com/373812
 class SmoothnessToughWebGLCases(benchmark.Benchmark):
   test = smoothness.Smoothness
   page_set = page_sets.ToughWebglCasesPageSet
diff --git a/tools/perf/benchmarks/startup.py b/tools/perf/benchmarks/startup.py
index 41b08f0..5a37272 100644
--- a/tools/perf/benchmarks/startup.py
+++ b/tools/perf/benchmarks/startup.py
@@ -60,9 +60,6 @@
 
 @benchmark.Enabled('has tabs')
 @benchmark.Enabled('win', 'linux', 'mac')
-# TODO(erikchen): Investigate source of 30s browser hang on startup.
-# http://crbug.com/473827
-@benchmark.Disabled
 class StartupLargeProfileColdBlankPage(_StartupCold):
   """Measures cold startup time with a large profile."""
   tag = 'cold'
@@ -73,15 +70,15 @@
     super(StartupLargeProfileColdBlankPage, self).__init__(max_failures)
     self.generated_profile_archive = "large_profile.zip"
 
+  def CustomizeBrowserOptions(self, options):
+    options.browser_startup_timeout = 10 * 60
+
   @classmethod
   def Name(cls):
     return 'startup.large_profile.cold.blank_page'
 
 @benchmark.Enabled('has tabs')
 @benchmark.Enabled('win', 'linux', 'mac')
-# TODO(erikchen): Investigate source of 30s browser hang on startup.
-# http://crbug.com/473827
-@benchmark.Disabled
 class StartupLargeProfileWarmBlankPage(_StartupWarm):
   """Measures warm startup time with a large profile."""
   tag = 'warm'
@@ -92,6 +89,9 @@
     super(StartupLargeProfileWarmBlankPage, self).__init__(max_failures)
     self.generated_profile_archive = "large_profile.zip"
 
+  def CustomizeBrowserOptions(self, options):
+    options.browser_startup_timeout = 10 * 60
+
   @classmethod
   def Name(cls):
     return 'startup.large_profile.warm.blank_page'
diff --git a/tools/perf/measurements/oilpan_gc_times_unittest.py b/tools/perf/measurements/oilpan_gc_times_unittest.py
index 45f1e56..8e9a2cbd 100644
--- a/tools/perf/measurements/oilpan_gc_times_unittest.py
+++ b/tools/perf/measurements/oilpan_gc_times_unittest.py
@@ -19,9 +19,8 @@
         'file://blank.html', page_set, page_set.base_dir)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction('ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class OilpanGCTimesTestData(object):
diff --git a/tools/perf/measurements/task_execution_time_unittest.py b/tools/perf/measurements/task_execution_time_unittest.py
index e2137e1..827377f 100644
--- a/tools/perf/measurements/task_execution_time_unittest.py
+++ b/tools/perf/measurements/task_execution_time_unittest.py
@@ -21,10 +21,8 @@
         'file://blank.html', page_set, base_dir)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class TaskExecutionTimeUnitTest(page_test_test_case.PageTestTestCase):
diff --git a/tools/perf/page_sets/key_desktop_sites.py b/tools/perf/page_sets/key_desktop_sites.py
index 77e3465..7e9b0cc6 100644
--- a/tools/perf/page_sets/key_desktop_sites.py
+++ b/tools/perf/page_sets/key_desktop_sites.py
@@ -13,10 +13,8 @@
     self.archive_data_file = 'data/key_desktop_sites.json'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class FacebookPage(KeyDesktopSitesPage):
@@ -45,10 +43,8 @@
     self.credentials = 'google'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.WaitForJavaScriptCondition(
         'window.gmonkey !== undefined && '
         'document.getElementById("gb") !== null')
@@ -82,10 +78,8 @@
     self.credentials = 'google'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.WaitForJavaScriptCondition(
         'document.getElementsByClassName("doclistview-list").length')
 
@@ -105,10 +99,8 @@
     self.credentials = 'google'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.WaitForJavaScriptCondition(
         'document.getElementsByClassName("kix-appview-editor").length')
 
diff --git a/tools/perf/page_sets/key_mobile_sites_smooth.py b/tools/perf/page_sets/key_mobile_sites_smooth.py
index 9136e6e..8f55751 100644
--- a/tools/perf/page_sets/key_mobile_sites_smooth.py
+++ b/tools/perf/page_sets/key_mobile_sites_smooth.py
@@ -8,10 +8,8 @@
 
 
 def _IssueMarkerAndScroll(action_runner):
-  interaction = action_runner.BeginGestureInteraction(
-      'ScrollAction')
-  action_runner.ScrollPage()
-  interaction.End()
+  with action_runner.CreateGestureInteraction('ScrollAction'):
+    action_runner.ScrollPage()
 
 
 def _CreatePageClassWithSmoothInteractions(page_cls):
@@ -73,72 +71,60 @@
 class GmailSmoothPage(key_mobile_sites_pages.GmailPage):
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollElement(element_function=(
-        'document.getElementById("views").childNodes[1].firstChild'))
-    interaction.End()
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollElement(element_function=(
-        'document.getElementById("views").childNodes[1].firstChild'))
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(element_function=(
+          'document.getElementById("views").childNodes[1].firstChild'))
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(element_function=(
+          'document.getElementById("views").childNodes[1].firstChild'))
 
 
 class GroupClonedSmoothPage(key_mobile_sites_pages.GroupClonedPage):
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage(
-        distance_expr='''
-            Math.max(0, 1250 + document.getElementById("element-19")
-                                       .contentDocument
-                                       .getElementById("element-22")
-                                       .getBoundingClientRect().top);''',
-        use_touch=True)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage(
+          distance_expr='''
+              Math.max(0, 1250 + document.getElementById("element-19")
+                                         .contentDocument
+                                         .getElementById("element-22")
+                                         .getBoundingClientRect().top);''',
+          use_touch=True)
 
 
 class GroupClonedListImagesPage(
   key_mobile_sites_pages.GroupClonedListImagesPage):
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage(
-        distance_expr='''
-            Math.max(0, 1250 +
-                document.getElementById("element-5")
-                        .getBoundingClientRect().top);''',
-        use_touch=True)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage(
+          distance_expr='''
+              Math.max(0, 1250 +
+                  document.getElementById("element-5")
+                          .getBoundingClientRect().top);''',
+          use_touch=True)
 
 class GoogleNewsMobile2SmoothPage(
   key_mobile_sites_pages.GoogleNewsMobile2Page):
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollElement(
-        element_function='document.getElementById(":5")',
-        distance_expr='''
-            Math.max(0, 2500 +
-                document.getElementById(':h').getBoundingClientRect().top)''',
-        use_touch=True)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(
+          element_function='document.getElementById(":5")',
+          distance_expr='''
+              Math.max(0, 2500 +
+                  document.getElementById(':h').getBoundingClientRect().top)''',
+          use_touch=True)
 
 
 class AmazonNicolasCageSmoothPage(
   key_mobile_sites_pages.AmazonNicolasCagePage):
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollElement(
-        selector='#search',
-        distance_expr='document.body.scrollHeight - window.innerHeight')
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(
+          selector='#search',
+          distance_expr='document.body.scrollHeight - window.innerHeight')
 
 
 class KeyMobileSitesSmoothPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/key_search_mobile.py b/tools/perf/page_sets/key_search_mobile.py
index 0535790e..2e5242e 100644
--- a/tools/perf/page_sets/key_search_mobile.py
+++ b/tools/perf/page_sets/key_search_mobile.py
@@ -14,10 +14,8 @@
     self.archive_data_file = 'data/key_search_mobile.json'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class KeySearchMobilePageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/mobile_memory.py b/tools/perf/page_sets/mobile_memory.py
index fc03bee..3e3e822d 100644
--- a/tools/perf/page_sets/mobile_memory.py
+++ b/tools/perf/page_sets/mobile_memory.py
@@ -47,25 +47,17 @@
         page_set=page_set)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.Wait(3)
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.Wait(3)
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.Wait(3)
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
     action_runner.WaitForJavaScriptCondition(
         'document.getElementById("rg_s").childElementCount > 300')
 
@@ -76,10 +68,8 @@
     super(ScrollPage, self).__init__(url=url, page_set=page_set)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class MobileMemoryPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/pathological_mobile_sites.py b/tools/perf/page_sets/pathological_mobile_sites.py
index da4134de..d508735e7 100644
--- a/tools/perf/page_sets/pathological_mobile_sites.py
+++ b/tools/perf/page_sets/pathological_mobile_sites.py
@@ -14,10 +14,8 @@
     self.archive_data_file = 'data/pathological_mobile_sites.json'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class PathologicalMobileSitesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/simple_mobile_sites.py b/tools/perf/page_sets/simple_mobile_sites.py
index 50fc0ae8..be6fc8da 100644
--- a/tools/perf/page_sets/simple_mobile_sites.py
+++ b/tools/perf/page_sets/simple_mobile_sites.py
@@ -26,10 +26,8 @@
 
   def RunPageInteractions(self, action_runner):
     # Make the scroll longer to reduce noise.
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage(direction='down', speed_in_pixels_per_second=300)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage(direction='down', speed_in_pixels_per_second=300)
 
 class SimpleMobileSitesPageSet(page_set_module.PageSet):
 
diff --git a/tools/perf/page_sets/simple_mobile_sites_fling.py b/tools/perf/page_sets/simple_mobile_sites_fling.py
index 1e56165..bc44765 100644
--- a/tools/perf/page_sets/simple_mobile_sites_fling.py
+++ b/tools/perf/page_sets/simple_mobile_sites_fling.py
@@ -25,13 +25,12 @@
     action_runner.Wait(5)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction('FlingAction')
-    # Swiping up induces a downward fling, and 500 pixels of touch scrolling
-    # runway ensures consistent fling velocities.
-    action_runner.SwipePage(direction='up',
-                            distance='500',
-                            speed_in_pixels_per_second=5000)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('FlingAction'):
+      # Swiping up induces a downward fling, and 500 pixels of touch scrolling
+      # runway ensures consistent fling velocities.
+      action_runner.SwipePage(direction='up',
+                              distance='500',
+                              speed_in_pixels_per_second=5000)
 
 class SimpleMobileSitesFlingPageSet(page_set_module.PageSet):
 
diff --git a/tools/perf/page_sets/top_10_mobile.py b/tools/perf/page_sets/top_10_mobile.py
index a9f5ae8..a18048c3 100644
--- a/tools/perf/page_sets/top_10_mobile.py
+++ b/tools/perf/page_sets/top_10_mobile.py
@@ -17,10 +17,8 @@
   def RunPageInteractions(self, action_runner):
     if self._run_no_page_interactions:
       return
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class Top10MobilePageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/tough_compositor_cases.py b/tools/perf/page_sets/tough_compositor_cases.py
index cbf585da4..66cc32c3 100644
--- a/tools/perf/page_sets/tough_compositor_cases.py
+++ b/tools/perf/page_sets/tough_compositor_cases.py
@@ -25,10 +25,8 @@
 
   def RunPageInteractions(self, action_runner):
     # Make the scroll longer to reduce noise.
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage(direction='down', speed_in_pixels_per_second=300)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage(direction='down', speed_in_pixels_per_second=300)
 
 class ToughCompositorWaitPage(ToughCompositorPage):
 
diff --git a/tools/perf/page_sets/tough_scheduling_cases.py b/tools/perf/page_sets/tough_scheduling_cases.py
index b30b8ee..275f470 100644
--- a/tools/perf/page_sets/tough_scheduling_cases.py
+++ b/tools/perf/page_sets/tough_scheduling_cases.py
@@ -14,10 +14,8 @@
     self.archive_data_file = 'data/tough_scheduling_cases.json'
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class Page1(ToughSchedulingCasesPage):
@@ -308,15 +306,13 @@
         page_set=page_set)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollElement(
-        selector='#card',
-        use_touch=True,
-        direction='up',
-        speed_in_pixels_per_second=150,
-        distance=400)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(
+          selector='#card',
+          use_touch=True,
+          direction='up',
+          speed_in_pixels_per_second=150,
+          distance=400)
 
 
 class EmptyTouchHandlerPage(ToughSchedulingCasesPage):
@@ -339,18 +335,14 @@
 
   def RunPageInteractions(self, action_runner):
     if self.bounce:
-      interaction = action_runner.BeginGestureInteraction(
-          'ScrollBounceAction')
-      action_runner.ScrollBouncePage()
-      interaction.End()
+      with action_runner.CreateGestureInteraction('ScrollBounceAction'):
+        action_runner.ScrollBouncePage()
     else:
-      interaction = action_runner.BeginGestureInteraction(
-          'ScrollAction')
-      # Speed and distance are tuned to run exactly as long as a scroll
-      # bounce.
-      action_runner.ScrollPage(use_touch=True, speed_in_pixels_per_second=400,
-                               distance=2100)
-      interaction.End()
+      with action_runner.CreateGestureInteraction('ScrollAction'):
+        # Speed and distance are tuned to run exactly as long as a scroll
+        # bounce.
+        action_runner.ScrollPage(use_touch=True, speed_in_pixels_per_second=400,
+                                 distance=2100)
 
 
 class SynchronizedScrollOffsetPage(ToughSchedulingCasesPage):
@@ -363,10 +355,8 @@
         page_set=page_set)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollBounceAction')
-    action_runner.ScrollBouncePage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollBounceAction'):
+      action_runner.ScrollBouncePage()
 
 
 class SecondBatchJsPage(ToughSchedulingCasesPage):
@@ -387,15 +377,14 @@
     # even while resources are being loaded.
     action_runner.WaitForJavaScriptCondition('window.__ready !== undefined')
 
-    interaction = action_runner.BeginGestureInteraction('LoadAction')
-    action_runner.ExecuteJavaScript('kickOffLoading()')
-    action_runner.WaitForJavaScriptCondition('window.__ready')
-    # Click one second after the resources have finished loading.
-    action_runner.Wait(1)
-    action_runner.TapElement(selector='input[id="run"]')
-    # Wait for the test to complete.
-    action_runner.WaitForJavaScriptCondition('window.__finished')
-    interaction.End()
+    with action_runner.CreateGestureInteraction('LoadAction'):
+      action_runner.ExecuteJavaScript('kickOffLoading()')
+      action_runner.WaitForJavaScriptCondition('window.__ready')
+      # Click one second after the resources have finished loading.
+      action_runner.Wait(1)
+      action_runner.TapElement(selector='input[id="run"]')
+      # Wait for the test to complete.
+      action_runner.WaitForJavaScriptCondition('window.__finished')
 
 
 class ToughSchedulingCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/tough_scrolling_while_zoomed_in_cases.py b/tools/perf/page_sets/tough_scrolling_while_zoomed_in_cases.py
index 14a79fdc..243e25a 100644
--- a/tools/perf/page_sets/tough_scrolling_while_zoomed_in_cases.py
+++ b/tools/perf/page_sets/tough_scrolling_while_zoomed_in_cases.py
@@ -30,14 +30,13 @@
     # 10,000 was chosen to complete this pre-step quickly.
 
     # Then start measurements
-    interaction = action_runner.BeginGestureInteraction('ScrollAction')
-    # And begin the diagonal scroll
-    action_runner.ScrollPage(
-        direction='downright',
-        speed_in_pixels_per_second=10000)
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      # And begin the diagonal scroll
+      action_runner.ScrollPage(
+          direction='downright',
           # 10,000 was chosen because it is fast enough to completely stress the
           # rasterization (on a Nexus 5) without saturating results.
-    interaction.End()
+          speed_in_pixels_per_second=10000)
 
 
 class ToughScrollingWhileZoomedInCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/tough_texture_upload_cases.py b/tools/perf/page_sets/tough_texture_upload_cases.py
index 1972896..2cef31d 100644
--- a/tools/perf/page_sets/tough_texture_upload_cases.py
+++ b/tools/perf/page_sets/tough_texture_upload_cases.py
@@ -15,10 +15,8 @@
         page_set=page_set)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class ToughTextureUploadCasesPageSet(page_set_module.PageSet):
diff --git a/tools/perf/page_sets/typical_25.py b/tools/perf/page_sets/typical_25.py
index cdd78bb..da527e28 100644
--- a/tools/perf/page_sets/typical_25.py
+++ b/tools/perf/page_sets/typical_25.py
@@ -16,10 +16,8 @@
   def RunPageInteractions(self, action_runner):
     if self._run_no_page_interactions:
       return
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class Typical25PageSet(page_set_module.PageSet):
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender.py b/tools/perf/profile_creators/fast_navigation_profile_extender.py
index 0beb62e0..7fa222a 100644
--- a/tools/perf/profile_creators/fast_navigation_profile_extender.py
+++ b/tools/perf/profile_creators/fast_navigation_profile_extender.py
@@ -83,6 +83,25 @@
     """
     pass
 
+  @property
+  def profile_path(self):
+    return self._profile_path
+
+  def _AddNewTab(self):
+    """Adds a new tab to the browser."""
+
+    # Adding a new tab requires making a request over devtools. This can fail
+    # for a variety of reasons. Retry 3 times.
+    retry_count = 3
+    for i in range(retry_count):
+      try:
+        self._navigation_tabs.append(self._browser.tabs.New())
+      except exceptions.Error:
+        if i == retry_count - 1:
+          raise
+      else:
+        break
+
   def _RefreshNavigationTabs(self):
     """Updates the member self._navigation_tabs to contain self._NUM_TABS
     elements, each of which is not crashed. The crashed tabs are intentionally
@@ -97,7 +116,7 @@
     self._navigation_tabs = live_tabs
 
     while len(self._navigation_tabs) < self._NUM_TABS:
-      self._navigation_tabs.append(self.browser.tabs.New())
+      self._AddNewTab()
 
   def _RemoveNavigationTab(self, tab):
     """Removes a tab which is no longer in a useable state from
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
index 8df3b30..447853f 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
@@ -49,7 +49,7 @@
        android_browser_backend_settings.ChromeBackendSettings,
        None],
   'android-chrome-dev':
-      ['com.chrome.dev',
+      ['com.google.android.apps.chrome_dev',
        android_browser_backend_settings.ChromeBackendSettings,
        None],
   'android-chrome-canary':
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
index 0e97113..480b6e7c 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
@@ -28,7 +28,7 @@
     self._browser_backend.devtools_client.CreateNewTab(timeout)
     return self[-1]
 
-  def CloseTab(self, tab_id, timeout=None):
+  def CloseTab(self, tab_id, timeout=30):
     """Closes the tab with the given debugger_url.
 
     Raises:
@@ -50,7 +50,7 @@
 
     util.WaitFor(lambda: tab_id not in self, timeout=5)
 
-  def ActivateTab(self, tab_id, timeout=None):
+  def ActivateTab(self, tab_id, timeout=30):
     """Activates the tab with the given debugger_url.
 
     Raises:
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_http.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_http.py
index 006d4e87..fecd768 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_http.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/devtools_http.py
@@ -69,6 +69,8 @@
     Raises:
       DevToolsClientConnectionError: If the connection fails.
     """
+    assert timeout
+
     if not self._conn:
       self._Connect(timeout)
 
diff --git a/tools/telemetry/telemetry/core/browser_options.py b/tools/telemetry/telemetry/core/browser_options.py
index 16fb585..9a800556 100644
--- a/tools/telemetry/telemetry/core/browser_options.py
+++ b/tools/telemetry/telemetry/core/browser_options.py
@@ -117,10 +117,6 @@
         help='Record profiling data using this tool. Supported values: ' +
              ', '.join(profiler_choices))
     group.add_option(
-        '--interactive', dest='interactive', action='store_true',
-        help='Let the user interact with the page; the actions specified for '
-             'the page are not run.')
-    group.add_option(
         '-v', '--verbose', action='count', dest='verbosity',
         help='Increase verbosity level (repeat as needed)')
     group.add_option('--print-bootstrap-deps',
diff --git a/tools/telemetry/telemetry/core/tab_list.py b/tools/telemetry/telemetry/core/tab_list.py
index da83d699..e692723 100644
--- a/tools/telemetry/telemetry/core/tab_list.py
+++ b/tools/telemetry/telemetry/core/tab_list.py
@@ -5,7 +5,7 @@
   def __init__(self, tab_list_backend):
     self._tab_list_backend = tab_list_backend
 
-  def New(self, timeout=None):
+  def New(self, timeout=30):
     return self._tab_list_backend.New(timeout)
 
   def __iter__(self):
diff --git a/tools/telemetry/telemetry/page/page_test.py b/tools/telemetry/telemetry/page/page_test.py
index bc5eb3e..019f492 100644
--- a/tools/telemetry/telemetry/page/page_test.py
+++ b/tools/telemetry/telemetry/page/page_test.py
@@ -207,14 +207,10 @@
 
   def RunPage(self, page, tab, results):
     # Run actions.
-    interactive = self.options and self.options.interactive
     action_runner = action_runner_module.ActionRunner(
         tab, skip_waits=page.skip_waits)
     self.WillRunActions(page, tab)
-    if interactive:
-      action_runner.PauseInteractive()
-    else:
-      page.RunPageInteractions(action_runner)
+    page.RunPageInteractions(action_runner)
     self.DidRunActions(page, tab)
     self.ValidateAndMeasurePage(page, tab, results)
 
diff --git a/tools/telemetry/telemetry/unittest_util/page_test_test_case.py b/tools/telemetry/telemetry/unittest_util/page_test_test_case.py
index 5e82a7a..8e1e1bdc 100644
--- a/tools/telemetry/telemetry/unittest_util/page_test_test_case.py
+++ b/tools/telemetry/telemetry/unittest_util/page_test_test_case.py
@@ -23,9 +23,8 @@
     super(BasicTestPage, self).__init__(url, page_set, base_dir)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction('ScrollAction')
-    action_runner.ScrollPage()
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
 
 
 class EmptyMetadataForTest(benchmark.BenchmarkMetadata):
diff --git a/tools/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py b/tools/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py
index a305f396..e3702e6 100644
--- a/tools/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/smooth_gesture_util_unittest.py
@@ -102,14 +102,12 @@
     super(ScrollingPage, self).__init__(url, page_set, base_dir)
 
   def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction')
-    # Add 0.5s gap between when Gesture records are issued to when we actually
-    # scroll the page.
-    time.sleep(0.5)
-    action_runner.ScrollPage()
-    time.sleep(0.5)
-    interaction.End()
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      # Add 0.5s gap between when Gesture records are issued to when we actually
+      # scroll the page.
+      time.sleep(0.5)
+      action_runner.ScrollPage()
+      time.sleep(0.5)
 
 
 class SmoothGestureTest(page_test_test_case.PageTestTestCase):
diff --git a/tools/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py b/tools/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py
index 4e5f386..68d4b8c 100644
--- a/tools/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py
+++ b/tools/telemetry/telemetry/web_perf/timeline_based_page_test_unittest.py
@@ -35,9 +35,8 @@
       action_runner.TapElement('#slow-button')
       action_runner.WaitForJavaScriptCondition('window.slowScriptDone')
     if self._trigger_scroll_gesture:
-      interaction = action_runner.BeginGestureInteraction('Scroll')
-      action_runner.ScrollPage()
-      interaction.End()
+      with action_runner.CreateGestureInteraction('Scroll'):
+        action_runner.ScrollPage()
 
 
 class TimelineBasedPageTestTest(page_test_test_case.PageTestTestCase):
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt
index 59e01847..272d0c9 100644
--- a/tools/valgrind/drmemory/suppressions.txt
+++ b/tools/valgrind/drmemory/suppressions.txt
@@ -766,3 +766,20 @@
 *!base::internal::InvokeHelper<>::MakeItSo
 *!base::internal::Invoker<>::Run
 *!content::ManifestFetcher::OnLoadComplete
+
+UNADDRESSABLE ACCESS
+name=http://crbug.com/476586
+*!blink::WebFrameWidgetImpl::beginFrame
+*!content::RenderWidgetCompositor::BeginMainFrame
+*!cc::LayerTreeHost::BeginMainFrame
+*!cc::ThreadProxy::BeginMainFrame
+*!base::internal::InvokeHelper<>::MakeItSo
+
+UNADDRESSABLE ACCESS
+name=http://crbug.com/476586b
+*!blink::PageWidgetDelegate::animate
+*!blink::WebFrameWidgetImpl::beginFrame
+*!content::RenderWidgetCompositor::BeginMainFrame
+*!cc::LayerTreeHost::BeginMainFrame
+*!cc::ThreadProxy::BeginMainFrame
+*!base::internal::InvokeHelper<>::MakeItSo
diff --git a/tools/valgrind/gtest_exclude/browser_tests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/browser_tests.gtest-drmemory_win32.txt
index f5dcbb0d..ecb31e45 100644
--- a/tools/valgrind/gtest_exclude/browser_tests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/browser_tests.gtest-drmemory_win32.txt
@@ -137,6 +137,8 @@
 Pe*
 Plat*Bro*
 Policy*H*
+# PopupBlockerBrowserTest.TapGestureWithCtrlKey
+Pop*
 Port*
 Prefe*
 PrefsF*
diff --git a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
index 01234982..7946e976 100644
--- a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
@@ -10,7 +10,7 @@
 # http://crbug.com/416643
 LayerTreeHostCopyRequestTestMultipleRequests.GLRenderer_RunSingleThread
 LayerTreeHostCopyRequestTestMultipleRequests.SoftwareRenderer_RunSingleThread
-LayerTreeHostTestReadyToDrawNonEmpty.RunSingleThread_DelegatingRenderer_ImplSidePaint
+LayerTreeHostTestReadyToDrawNonEmpty.*
 LayerTreeHostTestMaxTransferBufferUsageBytes.*
 
 # http://crbug.com/430400
diff --git a/tools/valgrind/gtest_exclude/unit_tests.gtest-memcheck.txt b/tools/valgrind/gtest_exclude/unit_tests.gtest-memcheck.txt
index 4e2b8ec..bae79ce 100644
--- a/tools/valgrind/gtest_exclude/unit_tests.gtest-memcheck.txt
+++ b/tools/valgrind/gtest_exclude/unit_tests.gtest-memcheck.txt
@@ -16,3 +16,6 @@
 
 # Test fail: crbug.com/473689
 ManagePasswordsBubbleModelTest.CloseWithoutLogging
+
+# Test fail: crbug.com/476731
+PluginInfoMessageFilterTest.FindEnabledPlugin
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc
index aef3e9f..6b07c21 100644
--- a/ui/base/ime/input_method_chromeos.cc
+++ b/ui/base/ime/input_method_chromeos.cc
@@ -371,7 +371,10 @@
   if (client != GetTextInputClient())
     return;
 
-  if (event.type() == ET_KEY_PRESSED && !handled)
+  if (handled)
+    return;  // IME handled the key event. do not forward.
+
+  if (event.type() == ET_KEY_PRESSED)
     ProcessUnfilteredKeyPressEvent(event);
   else if (event.type() == ET_KEY_RELEASED)
     DispatchKeyEventPostIME(event);
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
index b5fc0d9..3327114 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
@@ -25,10 +25,6 @@
 #include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h"
 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
 
-// Severity level for general info logging purpose.
-#define GPROP_LOG DVLOG
-#define INFO_SEVERITY 1
-
 // GesturesProp implementation.
 //
 // Check the header file for its definition.
@@ -180,7 +176,7 @@
   void InitializeNumericalProperty(const T* init,
                                    const GesturesProp* default_property) {
     if (IsDefaultPropertyUsable(default_property)) {
-      GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
+      DVLOG(2) << "Default property found. Using its value ...";
       this->template SetNumericalValue(default_property->GetDoubleValue());
     } else {
       // To work with the interface exposed by the gesture lib, we have no
@@ -364,7 +360,7 @@
                                 const GesturesProp* default_property) {
     // Initialize the property value similar to the numerical types.
     if (IsDefaultPropertyUsable(default_property)) {
-      GPROP_LOG(INFO_SEVERITY) << "Default property found. Using its value ...";
+      DVLOG(2) << "Default property found. Using its value ...";
       *value_ = default_property->GetStringValue();
     } else {
       *value_ = init;
@@ -1002,13 +998,13 @@
        path = file_enum.Next()) {
     files.insert(path);
   }
-  GPROP_LOG(INFO_SEVERITY) << files.size() << " conf files were found";
+  DVLOG(2) << files.size() << " conf files were found";
 
   // Parse conf files one-by-one.
   for (std::set<base::FilePath>::iterator file_iter = files.begin();
        file_iter != files.end();
        ++file_iter) {
-    GPROP_LOG(INFO_SEVERITY) << "Parsing conf file: " << (*file_iter).value();
+    DVLOG(2) << "Parsing conf file: " << (*file_iter).value();
     std::string content;
     if (!base::ReadFileToString(*file_iter, &content)) {
       LOG(ERROR) << "Can't loading gestures conf file: "
@@ -1096,12 +1092,12 @@
               has_error = true;
               is_input_class_section = false;
             } else {
-              GPROP_LOG(INFO_SEVERITY) << "New InputClass section found";
+              DVLOG(2) << "New InputClass section found";
               has_checked_section_type = true;
             }
             break;
           } else if (next_is_identifier) {
-            GPROP_LOG(INFO_SEVERITY) << "Identifier: " << arg;
+            DVLOG(2) << "Identifier: " << arg;
             config->identifier = arg;
             next_is_identifier = false;
             break;
@@ -1191,8 +1187,7 @@
 internal::MatchCriteria* GesturePropertyProvider::CreateMatchCriteria(
     const std::string& match_type,
     const std::string& arg) {
-  GPROP_LOG(INFO_SEVERITY) << "Creating match criteria: (" << match_type << ", "
-                           << arg << ")";
+  DVLOG(2) << "Creating match criteria: (" << match_type << ", " << arg << ")";
   if (match_type == "MatchProduct")
     return new internal::MatchProduct(arg);
   if (match_type == "MatchDevicePath")
@@ -1222,8 +1217,7 @@
   // 5. The property is treated as numeric if and only if all of its elements
   //    (if any) are numerics. Otherwise, it will be treated as a string.
   // 6. A string property will be trimmed before storing its value.
-  GPROP_LOG(INFO_SEVERITY) << "Creating default property: (" << name << ", "
-                           << value << ")";
+  DVLOG(2) << "Creating default property: (" << name << ", " << value << ")";
 
   // Parse elements one-by-one.
   std::string delimiters(base::kWhitespaceASCII);
@@ -1264,7 +1258,7 @@
     property = new GesturesStringProp(name, NULL, value.c_str(), NULL);
   }
 
-  GPROP_LOG(INFO_SEVERITY) << "Prop: " << *property;
+  DVLOG(2) << "Prop: " << *property;
   // The function will always succeed for now but it may change later if we
   // specify some name or args as invalid.
   return property;
@@ -1272,18 +1266,16 @@
 
 void GesturePropertyProvider::SetupDefaultProperties(const DeviceId device_id,
                                                      const DevicePtr device) {
-  GPROP_LOG(INFO_SEVERITY) << "Setting up default properties for (" << device
-                           << ", " << device_id << ", " << device->info.name
-                           << ")";
+  DVLOG(2) << "Setting up default properties for (" << device << ", "
+           << device_id << ", " << device->info.name << ")";
 
   // Go through all parsed sections.
   internal::PropertiesMap& property_map =
       device_data_map_.get(device_id)->default_properties;
   for (size_t i = 0; i < configurations_.size(); ++i) {
     if (configurations_[i]->Match(device)) {
-      GPROP_LOG(INFO_SEVERITY) << "Conf section \""
-                               << configurations_[i]->identifier
-                               << "\" is matched";
+      DVLOG(2) << "Conf section \"" << configurations_[i]->identifier
+               << "\" is matched";
       for (size_t j = 0; j < configurations_[i]->properties.size(); j++) {
         GesturesProp* property = configurations_[i]->properties[j];
         // We can't use insert here because a property may be set for several
@@ -1366,7 +1358,7 @@
 
   // No need to manually delete the prop pointer as it is implicitly handled
   // with scoped ptr.
-  GPROP_LOG(3) << "Freeing Property: \"" << property->name() << "\"";
+  DVLOG(3) << "Freeing Property: \"" << property->name() << "\"";
   provider->DeleteProperty(GetDeviceId(device_data), property->name());
 }
 
@@ -1478,7 +1470,7 @@
   provider->RegisterDevice(device_id, GetDevicePointer(device_data));
 
   // First, see if the GesturesProp already exists.
-  GPROP_LOG(3) << "Creating Property: \"" << name << "\"";
+  DVLOG(3) << "Creating Property: \"" << name << "\"";
   GesturesProp* property = provider->FindProperty(device_id, name);
 
   // If so, delete it as we can't reuse the data structure (newly-created
@@ -1504,7 +1496,7 @@
   provider->AddProperty(GetDeviceId(device_data), name, property);
 
   // Log the creation.
-  GPROP_LOG(INFO_SEVERITY) << "Created active prop: " << *property;
+  DVLOG(3) << "Created active prop: " << *property;
 }
 
 GesturesProp* GesturesPropFunctionsWrapper::CreateIntSingle(void* device_data,
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index b3326dd..aee3c46 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -1512,7 +1512,7 @@
   const DTBGOPTS value_draw_options = {
     sizeof(DTBGOPTS),
     (bar_rect.right == value_rect.right && bar_rect.left != value_rect.left) ?
-        DTBG_MIRRORDC : 0,
+        DTBG_MIRRORDC : 0u,
     bar_rect
   };
   if (pre_vista) {
diff --git a/ui/ozone/platform/drm/gpu/drm_display_snapshot.cc b/ui/ozone/platform/drm/gpu/drm_display_snapshot.cc
index 96e0a2f..4c906542 100644
--- a/ui/ozone/platform/drm/gpu/drm_display_snapshot.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display_snapshot.cc
@@ -83,12 +83,7 @@
                       nullptr),
       drm_(drm),
       connector_(connector->connector_id),
-      crtc_(crtc->crtc_id),
-      dpms_property_(drm->GetProperty(connector, "DPMS")) {
-  if (!dpms_property_)
-    VLOG(1) << "Failed to find the DPMS property for connector "
-            << connector->connector_id;
-
+      crtc_(crtc->crtc_id) {
   ScopedDrmPropertyBlobPtr edid_blob(drm->GetPropertyBlob(connector, "EDID"));
 
   if (edid_blob) {
diff --git a/ui/ozone/platform/drm/gpu/drm_display_snapshot.h b/ui/ozone/platform/drm/gpu/drm_display_snapshot.h
index 62ad1fe..e79f9a4 100644
--- a/ui/ozone/platform/drm/gpu/drm_display_snapshot.h
+++ b/ui/ozone/platform/drm/gpu/drm_display_snapshot.h
@@ -26,7 +26,6 @@
   // configuring this display.
   uint32_t connector() const { return connector_; }
   uint32_t crtc() const { return crtc_; }
-  drmModePropertyRes* dpms_property() const { return dpms_property_.get(); }
 
   // DisplaySnapshot overrides:
   std::string ToString() const override;
@@ -35,7 +34,6 @@
   scoped_refptr<DrmDevice> drm_;
   uint32_t connector_;
   uint32_t crtc_;
-  ScopedDrmPropertyPtr dpms_property_;
   std::string name_;
   bool overscan_flag_;
 
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index 60b335b..9a1d7cc4 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -306,19 +306,7 @@
               << " connector=" << output.connector();
       return false;
     }
-
-    if (output.dpms_property()) {
-      output.drm()->SetProperty(output.connector(),
-                                output.dpms_property()->prop_id,
-                                DRM_MODE_DPMS_ON);
-    }
   } else {
-    if (output.dpms_property()) {
-      output.drm()->SetProperty(output.connector(),
-                                output.dpms_property()->prop_id,
-                                DRM_MODE_DPMS_OFF);
-    }
-
     if (!screen_manager_->DisableDisplayController(output.drm(),
                                                    output.crtc())) {
       VLOG(1) << "Failed to disable device="
diff --git a/ui/ozone/platform/drm/gpu/drm_util.cc b/ui/ozone/platform/drm/gpu/drm_util.cc
index 01b82a2..517a58c 100644
--- a/ui/ozone/platform/drm/gpu/drm_util.cc
+++ b/ui/ozone/platform/drm/gpu/drm_util.cc
@@ -136,18 +136,11 @@
   if (displays.empty())
     return;
 
-  ScopedDrmPropertyPtr dpms(drm->GetProperty(displays[0]->connector(), "DPMS"));
-
   screen_manager->AddDisplayController(drm, displays[0]->crtc()->crtc_id,
                                        displays[0]->connector()->connector_id);
-  if (screen_manager->ConfigureDisplayController(
-          drm, displays[0]->crtc()->crtc_id,
-          displays[0]->connector()->connector_id, gfx::Point(),
-          displays[0]->connector()->modes[0])) {
-    if (dpms)
-      drm->SetProperty(displays[0]->connector()->connector_id, dpms->prop_id,
-                       DRM_MODE_DPMS_ON);
-  }
+  screen_manager->ConfigureDisplayController(
+      drm, displays[0]->crtc()->crtc_id, displays[0]->connector()->connector_id,
+      gfx::Point(), displays[0]->connector()->modes[0]);
 }
 
 base::FilePath GetPrimaryDisplayCardPath() {
diff --git a/ui/platform_window/win/win_window.cc b/ui/platform_window/win/win_window.cc
index 9bc8892..debf105 100644
--- a/ui/platform_window/win/win_window.cc
+++ b/ui/platform_window/win/win_window.cc
@@ -115,7 +115,8 @@
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION("440919 WinWindow::OnMouseRange"));
 
-  MSG msg = { hwnd(), message, w_param, l_param, GetMessageTime(),
+  MSG msg = { hwnd(), message, w_param, l_param,
+              static_cast<DWORD>(GetMessageTime()),
               { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } };
   MouseEvent event(msg);
   if (IsMouseEventFromTouch(message))
diff --git a/ui/views/bubble/bubble_delegate_unittest.cc b/ui/views/bubble/bubble_delegate_unittest.cc
index 782c9e79..ec5ab144 100644
--- a/ui/views/bubble/bubble_delegate_unittest.cc
+++ b/ui/views/bubble/bubble_delegate_unittest.cc
@@ -242,6 +242,7 @@
   test::TestWidgetObserver bubble_observer(bubble_widget);
   EXPECT_FALSE(bubble_observer.widget_closed());
 
+  anchor_widget->Show();
   bubble_widget->Show();
   EXPECT_TRUE(bubble_widget->IsVisible());
   anchor_widget->SetBounds(gfx::Rect(10, 10, 100, 100));
diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc
index b73083e..32fabd78 100644
--- a/ui/views/controls/label_unittest.cc
+++ b/ui/views/controls/label_unittest.cc
@@ -177,9 +177,8 @@
   Label label;
   base::string16 test_text = base::UTF8ToUTF16("\xF0\x9D\x84\x9E");
   label.SetText(test_text);
-  label.SizeToPreferredSize();
-
   label.SetObscured(true);
+  label.SizeToPreferredSize();
   EXPECT_EQ(ASCIIToUTF16("*"), label.GetDisplayTextForTesting());
   EXPECT_EQ(test_text, label.text());
 }
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h
index 088de92..fa910d23 100644
--- a/ui/views/linux_ui/linux_ui.h
+++ b/ui/views/linux_ui/linux_ui.h
@@ -154,6 +154,9 @@
   // Updates the device scale factor so that the default font size can be
   // recalculated.
   virtual void UpdateDeviceScaleFactor(float device_scale_factor) = 0;
+
+  // Determines the device scale factor of the primary screen.
+  virtual float GetDeviceScaleFactor() const = 0;
 };
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index 019688fb..fc477e6 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -25,6 +25,7 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/x/x11_types.h"
+#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
@@ -35,21 +36,12 @@
 // in |Dispatch()|.
 const int64 kConfigureDelayMs = 500;
 
-// TODO(oshima): Consider using gtk-xft-dpi instead.
-float GetDeviceScaleFactor(int screen_pixels, int screen_mm) {
-  const int kCSSDefaultDPI = 96;
-  const float kInchInMm = 25.4f;
-
-  float screen_inches = screen_mm / kInchInMm;
-  float screen_dpi = screen_pixels / screen_inches;
-  float scale = screen_dpi / kCSSDefaultDPI;
-
-  return ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale));
-}
-
-float GetDeviceScaleFactor() {
-  gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
-  return display.device_scale_factor();
+double GetDeviceScaleFactor() {
+  float device_scale_factor = 1.0f;
+  if (views::LinuxUI::instance())
+    device_scale_factor =
+      views::LinuxUI::instance()->GetDeviceScaleFactor();
+  return device_scale_factor;
 }
 
 gfx::Point PixelToDIPPoint(const gfx::Point& pixel_point) {
@@ -71,8 +63,7 @@
   gfx::Display gfx_display(0, bounds_in_pixels);
   if (!gfx::Display::HasForceDeviceScaleFactor() &&
       !ui::IsDisplaySizeBlackListed(physical_size)) {
-    float device_scale_factor = GetDeviceScaleFactor(
-        width, physical_size.width());
+    const float device_scale_factor = GetDeviceScaleFactor();
     DCHECK_LE(1.0f, device_scale_factor);
     gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels);
   }
@@ -294,7 +285,9 @@
     has_work_area = true;
   }
 
-  float device_scale_factor = 1.0f;
+  // As per-display scale factor is not supported right now,
+  // the X11 root window's scale factor is always used.
+  const float device_scale_factor = GetDeviceScaleFactor();
   for (int i = 0; i < resources->noutput; ++i) {
     RROutput output_id = resources->outputs[i];
     gfx::XScopedPtr<XRROutputInfo,
@@ -321,14 +314,6 @@
       gfx::Display display(display_id, crtc_bounds);
 
       if (!gfx::Display::HasForceDeviceScaleFactor()) {
-        if (i == 0 && !ui::IsDisplaySizeBlackListed(
-            gfx::Size(output_info->mm_width, output_info->mm_height))) {
-          // As per display scale factor is not supported right now,
-          // the primary display's scale factor is always used.
-          device_scale_factor = GetDeviceScaleFactor(crtc->width,
-                                                     output_info->mm_width);
-          DCHECK_LE(1.0f, device_scale_factor);
-        }
         display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
       }
 
diff --git a/ui/views/window/custom_frame_view_unittest.cc b/ui/views/window/custom_frame_view_unittest.cc
index fe20bf9..1320f48f 100644
--- a/ui/views/window/custom_frame_view_unittest.cc
+++ b/ui/views/window/custom_frame_view_unittest.cc
@@ -196,7 +196,7 @@
             title_bounds().x());
 }
 
-// Tests that layouts occuring while maximized swap the maximize button for the
+// Tests that layouts occurring while maximized swap the maximize button for the
 // restore button
 TEST_F(CustomFrameViewTest, MaximizeRevealsRestoreButton) {
   Widget* parent = widget();
@@ -211,8 +211,15 @@
   parent->Maximize();
   view->Layout();
 
+#if defined(OS_MACOSX)
+  // Restore buttons do not exist on Mac. The maximize button is instead a kind
+  // of toggle, but has no effect on frame decorations.
+  EXPECT_FALSE(restore_button()->visible());
+  EXPECT_TRUE(maximize_button()->visible());
+#else
   EXPECT_TRUE(restore_button()->visible());
   EXPECT_FALSE(maximize_button()->visible());
+#endif
 }
 
 // Tests that when the parent cannot maximize that the maximize button is not
@@ -271,10 +278,18 @@
   parent->Maximize();
   view->Layout();
 
+#if defined(OS_MACOSX)
+  // On Mac, "Maximize" should not alter the frame. Only fullscreen does that.
+  EXPECT_EQ(close_button()->bounds().width(),
+            close_button_initial_bounds.width());
+  EXPECT_EQ(minimize_button()->bounds().width(),
+            minimize_button_initial_bounds.width());
+#else
   EXPECT_GT(close_button()->bounds().width(),
             close_button_initial_bounds.width());
   EXPECT_GT(minimize_button()->bounds().width(),
             minimize_button_initial_bounds.width());
+#endif
 }
 
 }  // namespace views
diff --git a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.html b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.html
index 32f5be49..d9905fe4 100644
--- a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.html
+++ b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.html
@@ -1,4 +1,5 @@
 <link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_onc/cr_onc_data.html">
 
 <polymer-element name="cr-network-icon">
   <template>
diff --git a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
index 322a7dc..35f5172 100644
--- a/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
+++ b/ui/webui/resources/cr_elements/cr_network_icon/cr_network_icon.js
@@ -119,11 +119,16 @@
    * network state.
    */
   networkStateChanged: function() {
-    this.iconType = getIconTypeFromNetworkType(this.networkState.data.Type);
     var params = /** @type {IconParams} */ {
-      showBadges: true,
-      showDisconnected: !this.isListItem,
-      strength: this.networkState.getStrength(),
+      showBadges: false,
+      showDisconnected: true,
+      strength: 0,
+    };
+    if (this.networkState) {
+      this.iconType = getIconTypeFromNetworkType(this.networkState.data.Type);
+      params.showBadges = true;
+      params.showDisconnected = !this.isListItem;
+      params.strength = this.networkState.getStrength();
     };
     this.setIcon_(params);
   },
diff --git a/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.css b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.css
new file mode 100644
index 0000000..828c09e
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.css
@@ -0,0 +1,56 @@
+/* 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. */
+
+#divOuter {
+  border-style: none;
+  display: flex;
+  flex-direction: row;
+  margin: 0;
+  padding: 4px;
+  width: 100%;
+}
+
+#divOuter:hover {
+  background-color: lightgrey;
+}
+
+#divIcon {
+  display: flex;
+  flex: 0 0 auto;
+  flex-direction: column;
+  justify-content: center;
+}
+
+#icon {
+  height: 32px;
+  width: 32px;
+}
+
+#divDetail {
+  display: flex;
+  flex: 1 0 auto;
+  flex-direction: row;
+}
+
+#divText {
+  display: flex;
+  flex: 1 0 auto;
+  flex-direction: column;
+  justify-content: center;
+}
+
+#networkName {
+  -webkit-margin-start: 8px;
+  font-size: 16px;
+}
+
+#networkState {
+  -webkit-margin-start: 8px;
+  color: grey;
+  font-size: 14px;
+}
+
+.connected {
+  font-weight: bold;
+}
diff --git a/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.html b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.html
new file mode 100644
index 0000000..cd790f5
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.html
@@ -0,0 +1,19 @@
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_network_icon/cr_network_icon.html">
+
+<polymer-element name="cr-network-list-item">
+  <template>
+    <link rel="stylesheet" href="cr_network_list_item.css">
+    <div id="divOuter">
+      <div id="divIcon">
+        <cr-network-icon id="icon" networkState="{{networkState}}">
+        </cr-network-icon>
+      </div>
+      <div id="divText">
+        <span id="networkName"></span>
+        <span id="networkState" hidden?="{{isListItem}}"></span>
+      </div>
+    </div>
+  </template>
+  <script src="cr_network_list_item.js"></script>
+</polymer-element>
diff --git a/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.js b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.js
new file mode 100644
index 0000000..39786b7f
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.js
@@ -0,0 +1,95 @@
+// 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.
+
+/**
+ * @fileoverview Polymer element for displaying information about a network
+ * in a list or summary based on ONC state properties.
+ */
+(function() {
+
+/**
+ * TODO(stevenjb): Replace getText with a proper localization function that
+ * handles string substitution.
+ * Performs argument substitution, replacing %1, %2, etc in 'text' with
+ * corresponding entries in |args|.
+ * @param {string} text The string to perform the substitution on.
+ * @param {Array<string>} args The arguments to replace %1, %2, etc with.
+ */
+function getText(text, args) {
+  var res = text;
+  if (!args)
+    return res;
+  for (var i = 0; i < args.length; ++i) {
+    var key = '%' + (i + 1);
+    res = res.replace(key, args[i]);
+  }
+  return res;
+}
+
+/**
+ * Returns the appropriate connection state text.
+ * @param {string} state The connection state.
+ * @param {string} name The name of the network.
+ */
+function getConnectionStateText(state, name) {
+  if (state == 'Connected')
+    return getText('Connected to %1', [name]);
+  if (state == 'Connecting')
+    return getText('Connecting to %1...', [name]);
+  if (state == 'NotConnected')
+    return getText('Not Connected');
+  return getText(state);
+};
+
+/**
+ * Polymer class definition for 'cr-network-list-item'.
+ * @element cr-network-list-item
+ */
+Polymer('cr-network-list-item', {
+  publish: {
+    /**
+     * The ONC data properties used to display the list item.
+     *
+     * @attribute networkState
+     * @type CrOncDataElement
+     * @default null
+     */
+    networkState: null,
+
+    /**
+     * If true, the element is part of a list of networks and only displays
+     * the network icon and name. Otherwise the element is assumed to be a
+     * stand-alone item (e.g. as part of a summary) and displays the name
+     * of the network type plus the network name and connection state.
+     *
+     * @attribute isListItem
+     * @type boolean
+     * @default false
+     */
+    isListItem: false,
+  },
+
+  /**
+   * Polymer networkState changed method. Updates the element based on the
+   * network state.
+   */
+  networkStateChanged: function() {
+    if (!this.networkState)
+      return;
+
+    var network = this.networkState.data;
+    var isDisconnected = this.networkState.disconnected();
+    if (this.isListItem) {
+      this.$.networkName.textContent = getText(network.Name);
+      this.$.networkName.classList.toggle('connected', !isDisconnected);
+    } else {
+      this.$.networkName.textContent = getText(network.Type);
+      this.$.networkName.classList.toggle('connected', false);
+      this.$.networkState.textContent =
+          getConnectionStateText(network.ConnectionState, network.Name);
+      this.$.networkState.classList.toggle('connected', !isDisconnected);
+    }
+  },
+});
+})();
diff --git a/ui/webui/resources/cr_elements/cr_onc/cr_onc_data.js b/ui/webui/resources/cr_elements/cr_onc/cr_onc_data.js
index 1d87b74..978532f4 100644
--- a/ui/webui/resources/cr_elements/cr_onc/cr_onc_data.js
+++ b/ui/webui/resources/cr_elements/cr_onc/cr_onc_data.js
@@ -37,6 +37,11 @@
     return this.data.ConnectionState == CrOnc.ConnectionState.CONNECTING;
   },
 
+  /** @return {boolean} True if the network is disconnected. */
+  disconnected: function() {
+    return this.data.ConnectionState == CrOnc.ConnectionState.NOT_CONNECTED;
+  },
+
   /** @return {number} The signal strength of the network. */
   getStrength: function() {
     var type = this.data.Type;
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index f1b89bf..960cc81 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -45,6 +45,15 @@
   <structure name="IDR_CR_ELEMENTS_CR_NETWORK_ICON_JS"
              file="../../webui/resources/cr_elements/cr_network_icon/cr_network_icon.js"
              type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_NETWORK_LIST_ITEM_CSS"
+             file="../../webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.css"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_NETWORK_LIST_ITEM_HTML"
+             file="../../webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.html"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_NETWORK_LIST_ITEM_JS"
+             file="../../webui/resources/cr_elements/cr_network_list_item/cr_network_list_item.js"
+             type="chrome_html" />
   <structure name="IDR_CR_ELEMENTS_CR_ONC_TYPES_JS"
              file="../../webui/resources/cr_elements/cr_onc/cr_onc_types.js"
              type="chrome_html" />
diff --git a/ui/webui/resources/js/compiled_resources.gyp b/ui/webui/resources/js/compiled_resources.gyp
index 668413a6..40b1571 100644
--- a/ui/webui/resources/js/compiled_resources.gyp
+++ b/ui/webui/resources/js/compiled_resources.gyp
@@ -50,7 +50,7 @@
       'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'],
     },
     {
-      'target_name': 'i18n_template2',
+      'target_name': 'i18n_template',
       'variables': {
         'depends': ['compiled_resources.gyp:load_time_data'],
       },
diff --git a/ui/webui/resources/js/cr/ui/list_selection_model.js b/ui/webui/resources/js/cr/ui/list_selection_model.js
index 60f4106c..5b143d6d 100644
--- a/ui/webui/resources/js/cr/ui/list_selection_model.js
+++ b/ui/webui/resources/js/cr/ui/list_selection_model.js
@@ -143,6 +143,9 @@
      * Selects all indexes.
      */
     selectAll: function() {
+      if (this.length === 0)
+        return;
+
       this.selectRange(0, this.length - 1);
     },